Несколько вопросов по управлению памятью с участием деструкторов классов и оператора удаления? - PullRequest
3 голосов
/ 06 июля 2011

После прочтения некоторых руководств мне все еще неясно, по каким вопросам об управлении памятью в C ++.

1.когда класс, объявленный с оператором new, выходит из области видимости, вызывается ли его деструктор и освобождается ли память?Нужно ли вызывать оператор удаления, чтобы освободить память класса и вызвать его деструктор?

class Test{};

void newTest(){
    Test *t = new Test;
}

int main()
{
    newTest();
    return 0;
}

2.Переменные (например, вектор), объявленные с новым ключевым словом, освобождаются, когда класс, в котором они находятся, уничтожен?Должны ли эти переменные быть явно удалены в деструкторе класса?

class Test{
    vector<int> *vec;
public:
    Test();
};

Test::Test(){
    *vec = new vector<int>;
}

void newTest(){
    Test t;
}

int main()
{
    newTest();
    return 0;
}

3.То же, что и вопрос 2, но с обычными переменными, объявленными в стеке.В следующем примере удаляется вектор vec, когда t выходит из области видимости?

class Test{
    vector<int> vec;
};

void newTest(){
    Test t;
}

int main()
{
    newTest();
    return 0;
}

4.И, наконец, какой лучший способ объявить вектор в классе, обычно (в стеке) или с новым ключевым словом (в куче)?

Ответы [ 6 ]

2 голосов
/ 06 июля 2011

Звучит так, как будто вы пришли из Java-фона, поэтому вещи с new работают немного по-другому в C ++.

  1. Память, выделенная с помощью new, должна быть явно уничтоженас delete.В этом примере есть утечка памяти.

  2. Как и в вопросе 1, необходимо удалить память в деструкторе класса, чтобы восстановить ее.

  3. Inв этом случае вектор будет уничтожен автоматически.

  4. Внутри класса всегда предпочитайте хранить членов по значению (в отличие от новых), если у вас нет особой причины поступать иначе.Тот факт, что он хранится в классе по значению, не означает, что он находится в стеке, он находится в той же области памяти, что и содержащий класс.

Наконец, обратите внимание, что в C ++ вы можетеиспользуйте RAII, чтобы сделать управление памятью намного менее подверженным ошибкам.Взгляните, например, на shared_ptr, scoped_ptr и unique_ptr.Все они обрабатывают автоматически, освобождая new выделенную память при необходимости.

1 голос
/ 06 июля 2011
  1. когда класс, объявленный с новым оператором выходит из области видимости, вызывается ли его деструктор и освобождается ли память?Необходимо ли вызывать оператор удаления, чтобы освободить память класса и вызвать его деструктор?

Сначала немного терминологии:

Переменная является либо объектом, либоуказатель (класс не объявляется с новым указателем).
Объекты автоматически уничтожаются при выходе из области видимости.
Указатели НЕ автоматически удаляются.

Второе предпочтениеподчиняется указателям

void newTest()
{
    Test t;  // t created here and automatically destroyed.
}

Третье.Если вы должны создать указатель.Поместите его в интеллектуальный указатель.

void newTest()
{
    std::auto_ptr<Test> t(new Test());  // t created here and automatically destroyed.
                                        // Which will call delete on the contained pointer.
}

Ответ на вопрос 1:

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

  1. Являются ли переменные (например, вектор) объявленными с новым ключевым словом, освобожденным, когда класс, в котором они находятсявнутри разрушено?Должны ли эти переменные быть явно удалены в деструкторе класса?

Терминология

Вы имеете в виду pointers initialized with new, а не declared with the new keyword

Ответ наВопрос 2.

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

class Test
{
    vector<int>                   vectorObject;
    std::auto_ptr<vector<int> >   vectorPtrInSmartPointer;
    vector<int>*                  vectorRaw
public:
    Test();
    ~Test();
private:
    Test(Test const& copy);
    Test& operator=(Test const& copy);
};

Test::Test()
   : vectorPtrInSmartPointer(new vector<int>())  // Need to initialize the smart pointer.
   , vectorRaw(new vector<int>)                  // Need to initialize the RAW pointer
{
   // Note the vectorObject was automatically created.
}
Test::~Test()
{
   delete vectorRaw;                             // Need to manually release the RAW pointer.
}
// Smart pointer and object auto released.

Примечание. Поскольку класс Test содержит указатель RAW (вектор *), мне пришлось вручную отключить конструктор копирования Test::Test(Test const&) иоператор присваивания Test& operator=(Test const&).Это сделано для того, чтобы обеспечить соблюдение правила 3.

  1. То же, что и вопрос 2, но с обычными переменными, объявленными в стеке.В следующем примере, удаляется ли вектор vec, когда t выходит из области видимости?

Да.

  1. Наконец, что является лучшим способомобъявить вектор в классе, обычно (в стеке) или с новым ключевым словом (в куче)?

Вызов его стеком / кучей скрывает детали (как членможет быть автоматическим членом динамически размещаемого объекта).Предпочитаю думать о них как automatic variables и dynamic variables.automatic variables автоматически уничтожаются, когда уничтожается область их содержания.Так что для переменных функции это означает, что когда вы покидаете функцию.Для членов класса это означает, когда объект уничтожен.Выбор будет состоять в том, чтобы использовать automatic variables (т.е. не выделять новые).

1 голос
/ 06 июля 2011
  1. Нет, объект, созданный с помощью new, не уничтожается автоматически, когда ваш указатель выходит из области видимости. Вам необходимо явно delete любой такой объект.
  2. Тот же ответ. Любая память, выделенная с помощью new, должна быть явно освобождена. Большинство хорошо написанных классов сделают это для вас в деструкторе. Если вы пишете класс, убедитесь, что вы очистили любую память, которую использует класс.
  3. Этот объект уничтожается автоматически для вас. И, если он хорошо написан, он автоматически уничтожит любую память, которую выделит.
  4. Большинство экземпляров классов следует размещать как локальные переменные, поскольку это обеспечит их автоматическую очистку. Однако, если они вам нужны глобально или как часть класса, вам нужно будет объявить их соответствующим образом.
1 голос
/ 06 июля 2011
  1. Нет, деструктор не вызывается и память не освобождается.Вы должны delete объект.
  2. Нет, переменные не освобождаются.Вы должны удалить их в деструкторе.
  3. Да, vec удаляется (вызывается деструктор и освобождается память).
  4. Обычно в стеке лучше, когда это возможно.Это может быть невозможно, если, например, право собственности на объект передается другому объекту.Кроме того, не следует выделять слишком много памяти в стеке (ОС имеет ограничения на это), и в этом случае объект должен размещаться в куче.
0 голосов
/ 06 июля 2011

Вы должны предпочесть сказать, что объект имеет динамическое хранилище, а не "находиться в куче". Аналогично, используйте «автоматическое хранение» вместо «в стеке».

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

Если объект имеет автоматическое хранение, то компилятор автоматически вызывает его деструктор, когда объект выходит из области видимости (в конце содержащего его блока).

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

Итак, если вы используете new, вам нужно иметь соответствующий delete. new[] должен соответствовать delete[].

(«Умный указатель» весьма полезен, поскольку он поможет вам избавиться от необходимости отслеживать new и delete. Посмотрите!).

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

Вывод:

Если возможно, попробуйте использовать умные указатели или коллекции. В противном случае, убедитесь, что вы delete, что вы new. (И если вы напишите класс, выделите в конструкторе любые динамические члены и удалите их в деструкторе класса.)

0 голосов
/ 06 июля 2011

Эти два простых правила должны ответить на все вопросы:

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