Приведет ли это к утечке памяти в C ++? - PullRequest
6 голосов
/ 24 января 2009

У меня есть сомнения в управлении памятью на C ++, которые (очевидно) связаны со ссылками и указателями. Предположим, у меня есть класс Class с методом my_method:

OtherClass& Class::my_method( ... ) {
    OtherClass* other_object = new OtherClass( ... );
    return *other_object;
}

Между тем в соседнем фрагменте кода:

{
    Class m( ... );
    OtherClass n;
    n = m.my_method( ... );
}

Итак, я знаю, что есть общее правило для указателей (~ "что-нибудь новенькое, должно быть delete-d"), чтобы избежать утечек памяти. Но в основном я беру ссылку на мой объект, выделенный в куче, поэтому, когда n выходит из области видимости, не должен ли вызываться деструктор OtherClass, освобождая память, ранее указанную other_object? Итак, в конце концов, реальный вопрос: приведет ли это к утечке памяти?

Спасибо.

Ответы [ 7 ]

12 голосов
/ 24 января 2009

Да, это приведет к утечке памяти.

В инструкции return разыменовывается новый объект, который вы создали. Компилятор вызовет оператор присваивания как часть возвращаемого и скопирует СОДЕРЖАНИЕ вашего нового объекта объекту, которому он назначен в вызывающем методе.

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

Почему бы не вернуть указатель и не управлять им таким образом?

6 голосов
/ 24 января 2009

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

OtherClass Class::my_method( ... ) {
    return OtherClass( ... );
}

Затем в вызывающем коде вы можете сконструировать новый объект следующим образом.

{
    Class m( ... );
    OtherClass n( m.mymethod( ... ) );
}

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

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

1011 * Е.Г. *

{
    Class m( ... );

    // Trust me I know what I'm doing, I'll delete this object later...
    OtherClass* n = new OtherClass( m.mymethod( ... ) );
}
5 голосов
/ 24 января 2009

Вызов деструктора и освобождение памяти - это две разные вещи в C ++.

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

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


В качестве предложения, когда вы чувствуете, что полностью поняли указатели в C ++, посмотрите на умные указатели, например, продвигайте умные указатели, чтобы упростить вашу жизнь управления памятью в C ++. (например, см. статью здесь для ознакомления)
2 голосов
/ 24 января 2009

Если вы настаиваете на выделении стека, не используйте оператор new в my_method() и вместо этого передайте ссылку на n, то есть:

void Class::my_method( OtherClass& other_object, ... ) {
    other_object.init( ... );
}

Затем позвоните my_method() так:

{
    Class m( ... );
    OtherClass n;
    m.my_method( n, ... );
}

Чтобы этот шаблон работал, Class должен реализовывать некоторый метод init(), который позволяет правильно инициализировать объекты без вызова конструктора.

2 голосов
/ 24 января 2009

Если возможно, вы можете использовать std :: auto_ptr или boost / c0x shared_ptr для упрощения управления памятью.

2 голосов
/ 24 января 2009

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

Я думаю, что вы, вероятно, можете сделать это:

Объявите другой класс DummyClass, который содержит открытый член, который является указателем на OtherClass объект. В конструкторе DummyClass очистите указатель на NULL. В вашей функции объявите объект типа DummyClass и указатель на его член для создания другого объекта типа OtherClass. Затем в деструкторе DummyClass проверьте, равен ли указатель NULL, если нет, удалите его. Таким образом, ваш объект будет очищен автоматически, когда объект DummyClass выходит из области видимости.

2 голосов
/ 24 января 2009

У вас есть 2 объекта OtherClass:

Одним из них является n, которое создается в стеке и успешно удаляется, когда выходит из области видимости.

Другой - тот, который вы создаете в куче внутри my_method. Этот никогда не удаляется, и это приведет к утечке памяти.

...