2009-03-25 2 views
2

Ниже, я не Объявление my_ints в качестве указателя. Я не знаю, где будет выделена память. Пожалуйста, просветите меня здесь!В чем разница между этими двумя классами?

#include <iostream> 
#include <vector> 

class FieldStorage 
{ 
private: 
    std::vector<int> my_ints; 

public: 
    FieldStorage() 
    { 
     my_ints.push_back(1); 
     my_ints.push_back(2); 
    } 

    void displayAll() 
    { 
     for (int i = 0; i < my_ints.size(); i++) 
     { 
      std::cout << my_ints[i] << std::endl; 
     } 
    } 
}; 

И здесь, я объявляю поле my_ints как указатель:

#include <iostream> 
#include <vector> 

class FieldStorage 
{ 
private: 
    std::vector<int> *my_ints; 

public: 
    FieldStorage() 
    { 
     my_ints = new std::vector<int>(); 
     my_ints->push_back(1); 
     my_ints->push_back(2); 
    } 

    void displayAll() 
    { 
     for (int i = 0; i < my_ints->size(); i++) 
     { 
      std::cout << (*my_ints)[i] << std::endl; 
     } 
    } 

    ~FieldStorage() 
    { 
     delete my_ints; 
    } 
}; 

main() функцию для проверки:

int main() 
{ 
    FieldStorage obj; 
    obj.displayAll(); 
    return 0; 
} 

Оба производит один и тот же результат. Какая разница?

+0

Спасибо всем, кто указал на утечку памяти ... и на все ответы! – Srikanth

ответ

13

С точки зрения управления памятью эти два класса практически идентичны. Несколько других респондентов предположили, что существует разница между ними в том, что они выделяют хранилище в стеке и другие в куче, но это не обязательно верно, и даже в тех случаях, когда это правда, это ужасно вводит в заблуждение. В действительности, все, что отличается, - это где метаданные для vector; фактическое базовое хранилище в vector выделяется из кучи независимо.

Это немного сложно увидеть, потому что вы используете std::vector, поэтому конкретные детали реализации скрыты. Но в принципе, std::vector реализуется следующим образом:

template <class T> 
class vector { 
public: 
    vector() : mCapacity(0), mSize(0), mData(0) { } 
    ~vector() { if (mData) delete[] mData; } 
    ... 
protected: 
    int mCapacity; 
    int mSize; 
    T *mData; 
}; 

Как вы можете видеть, сам vector класс имеет только несколько членов - емкость, размер и указатель на динамически выделенный блок памяти, который будет хранить фактический содержимое вектора.

В вашем примере единственное отличие заключается в том, где происходит хранение для этих нескольких полей. В первом примере хранилище выделяется из любого хранилища, которое вы используете для своего содержащего класса, - если оно выделено в кучу, так же будут и эти несколько бит vector. Если ваш контейнер является выделенным стекем, так же будут и эти несколько бит vector.

Во втором примере эти биты vector всегда выделены в кучу.

В обоих примерах фактическое мясоvector - его содержимое выделено из кучи, и вы не можете изменить это.

Все остальные уже указали, что у вас есть утечка памяти во втором примере, и это также верно. Обязательно удалите вектор в деструкторе вашего класса контейнера.

+0

Собственно, они правы - в первом случае векторный объект выделяется в стеке - тот факт, что он внутренне выделяет память в куче, не имеет ничего общего с этим фактом. –

+0

... не указательный элемент, а не «какая разница в этом конкретном образце кода». Конечно, вы абсолютно правильно относитесь к образцу кода, и если это так, я извиняюсь. :-) – Dan

+0

@Nemanja: Кажется, я обратился к этому в ответ, и Дэн еще раз пояснил мою мысль. В любом случае, самое главное, где основная часть использования памяти исходит. Это векторное содержимое, а не метаданные, а содержимое всегда выделено в кучу. –

4

Вы должны освободить (чтобы предотвратить утечку памяти) память, выделенную для вектора во втором случае в деструкторе FieldStorage.

FieldStorage::~FieldStorage() 
{ 
    delete my_ints; 
} 
+0

+1. Или еще лучше использовать интеллектуальный указатель, такой как std :: auto_ptr, boost :: scoped_ptr или boost :: shared_ptr. В любом случае необходимо будет учитывать конструктор копирования и назначение копии. Предоставленные компилятором значения по умолчанию, скорее всего, не будут достаточными. –

+0

Ну, я не уверен, действительно ли мне это нужно. Если я правильно помню, я прочитал в Effective C++, что деструктор по умолчанию вызывает все деструкторы полей класса. Это правда или я понял? – Srikanth

+0

Он работает для вашего первого случая. Во втором случае деструктор класса освободит память, которая будет получена самим указателем, но не освободит память, которая была выделена новым - вы должны это сделать сами. Просто используйте свой первый случай, если my_ints не будет жить дольше, чем ваш FieldStorage. –

1

Первый способ является предпочтительным для использования, вам не нужен указатель на вектор и забыл его удалить.

+0

Я бы сказал «предпочтительнее», а не «правильно». –

+0

Я не вижу причин использовать «вектор *» в качестве члена класса в остроконечном случае. Итак, «правильно» отражает то, как вы должны это делать. Это может быть «неправильно» и все еще выполнять эту работу. –

2

В первом примере объект выделяется в стеке.

Второй пример: объект выделяется в куче, а указатель на эту память хранится в стеке.

+0

Если используется «FieldStorage obj = new FieldStoreage();» как это повлияет на ваш ответ? Все примитивы выделены в стек? как насчет тех, которые добавлены в вектор в ctor, находятся ли они в стеке? –

+0

@Binary Worrier: «FieldStorage obj = new FieldStorage();» это вздор. Возвращаемым типом «new FieldStorage()» является «FieldStorage *». Что касается других вопросов, я не знаю. Прошло некоторое время с тех пор, как я использовал C++. – Welbog

+0

Если вы выделяете FieldStorage в куче, вектор использует часть этого распределения кучи. Если вы выделяете FieldStorage в стеке, вектор использует часть распределения этого стека. Если вы определите глобальный FieldStorage ... ну, вы получите изображение. – bk1e

3

Как отметил Николай Голубев, вам нужно удалить вектор во втором случае.

Первый, возможно, построит более быстрый код, так как оптимизатор знает полный размер поля FieldStorage, включая вектор, и может выделять достаточно памяти в одном распределении для обоих.

Для вашей второй реализации требуется два отдельных распределения для построения объекта.

1

разница заключается в том, что вторая динамически распределяет вектор. Есть несколько отличий:

  • вы должны освободить память, занятую векторный объектом (сам векторного объектом, а не объект хранится в векторе, так как он правильно обрабатывается вектор). вы должны использовать некоторый умный указатель, чтобы сохранить вектор или сделать (например, в деструкторе):

    delete my_ints;

  • первый, вероятно, более эффективен, поскольку объект выделяется в стеке.

  • доступ к методу вектора имеют различный синтаксис :)

0

В первом случае станд :: вектор кладется прямо в классе (и обрабатывает любое распределение памяти и освобождение его необходимо вырастить и сжать вектор и освободить память, когда ваш объект будет уничтожен, он абстрагирует управление памятью от вас, так что вам не придется с этим справляться). Во втором случае вы явно выделяете хранилище для std :: vector в кучу где-то и забываете когда-либо удалять его, поэтому ваша программа будет утечка памяти.

+0

«и он обрабатывает любое распределение памяти и освобождение, которое нужно вырастить и сжимать вектор». , , происходит в обоих случаях. –

3

Я думаю, что вы действительно ищете разницу между Stack and the Heap.

Первый помещается в стек, а второй - в кучу.

+0

Какое имя «член в стеке»? Это звучит странно. Как мы можем назвать это «стек», если мы не знаем, как будет выделен экземпляр объекта? –

+0

-1 Неверный. В первом случае my_ints выделяется как часть объекта. Будь то в стеке или в куче, зависит от того, как был распределен сам экземпляр. Во втором случае это отдельное распределение кучи. –

+0

Фред, я думаю, ты прав. Это имело бы смысл. Благодарю. –

1

Первая версия FieldStorage содержит вектор. Размер класса FieldStorage включает в себя достаточное количество байтов для хранения вектора. Когда вы создаете FieldStorage, вектор строится непосредственно перед тем, как будет выполнен конструктор FieldStorage. Когда FieldStorage разрушается, то и вектор.

Это не обязательно выделяет вектор в стеке; если вы куча-выделите объект FieldStorage, тогда пространство для вектора будет получено из этого распределения кучи, а не из стека. Если вы определяете глобальный объект FieldStorage, то пространство для вектора происходит не из ни стека, ни кучи, а скорее из пространства, предназначенного для глобальных объектов (например, раздел или .bss на некоторых платформах).

Обратите внимание, что вектор выполняет выделение кучи для хранения фактических данных, поэтому он может содержать только несколько указателей или указатель и пару длин, но он может содержать все, что ему требуется для выполнения STL-реализации вашего компилятора.

Вторая версия FieldStorage содержит указатель на вектор. Размер класса FieldStorage включает место для указателя на вектор, а не на фактический вектор. Вы выделяете хранилище для вектора, используя новое в теле конструктора FieldStorage, и вы уничтожаете это хранилище при уничтожении FieldStorage, потому что вы не определили деструктор, который удаляет вектор.

0

Размер объекта будет отличаться. Во втором случае Vector <> * занимает только размер указателя (4 байта на 32-битных машинах). В первом случае ваш объект будет больше.

0

Одно практическое отличие состоит в том, что в вашем втором примере вы никогда не выпускаете ни памяти для вектора, ни его содержимого (потому что вы его не удаляете и поэтому вызываете его деструктор).

Но первый пример автоматически уничтожит вектор (и освободит его содержимое) при уничтожении вашего объекта FieldStorage.

Смежные вопросы