2011-12-20 3 views
12

Я немного новичок в C++ и занимаюсь программированием в Obj-C и Java до сих пор.C++: об управлении памятью

Скажем, у меня есть класс:

class Person { 

private: 
    Wife *current_wife; 
    //..... 
}; 

Так OBV мне нужно реализовать метод установки, чтобы изменить переменную экземпляра Wife.

Как это:

Person::SetCurrentWife (Wife *new_wife) { 

    current_wife = new_wife; 
} 

Это будет shalllow копия.

Так где-то из основного цикла или что-то я называю:

Person *some_person = new Person(); 
... 
Wife *wife = new Wife(); 
some_person->SetCurrentWife(wife); 

Так что я запутался: будет ли утечка памяти здесь? Должен ли я удалить объект жены здесь или в деструкторе человека? В Obj-C я бы освободил нынешнюю жену, а затем отправил сообщение о сохранении в женский объект выше, но каков правильный способ использования методов setter в C++?

+10

Поскольку вы новичок в C++, прислушайтесь к этому совету: никогда не используйте 'new',' delete' или указатели. В качестве исключения вы * можете * использовать 'new' внутри конструктора интеллектуального указателя, но только после того, как вы определили, что вам действительно требуется динамическое хранилище. –

+4

Kerrek забыл порекомендовать [хорошая вводная книга на C++] (http://stackoverflow.com/q/388242/46642) :) –

+4

В OO вам не нужен сеттер, вам нужны функции, позволяющие объекту что-то делать (развод, повторный брак). Сети могут быть удобными, они не являются объектами. – stefaanv

ответ

4

Вы должны использовать умный указатель.

Если вы используете обычный указатель, соблюдайте осторожность!

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

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

+3

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

+0

@JamesKanze, так почему это проблема? Используете ли вы умные указатели, чтобы вы заранее удалили объект жены или удалили его после смерти человека? –

+0

зависит от умного указателя, но если вы удаляете что-то, управляемое любым из стандартных интеллектуальных указателей, вы находитесь в неопределенном поведении. Если вы используете 'shared_ptr', особенно срок жизни объекта находится вне ваших рук. –

1

Интеллектуальные указатели могут помочь вам

using boost::shared_ptr; // or std::shared_ptr, or std::tr1::shared_ptr or something like this 
class Person { 

private: 
    shared_ptr<Wife> current_wife; 
    //..... 
}; 

Person::SetCurrentWife (shared_ptr<Wife> new_wife) { 

    current_wife = new_wife; 
} 

И теперь вы не должны удалить любую жену вообще.

shared_ptr<Person> some_person (new Person); 
... 
shared_ptr<Wife> wife (new Wife); 
some_person->SetCurrentWife(wife); 
+3

Это звучит как рецепт катастрофы. Есть только очень мало случаев, когда 'shared_ptr' работает, и я подозреваю, что это не один из них. По двум причинам: во-первых, вероятно, что «Жена» также знает, кем она является женой, поэтому у вас есть цикл (и вам нужен цикл, поскольку, если что-то происходит с «Женой», необходимо сообщить об этом супруг). И, во-вторых, срок жизни «Жены», безусловно, не зависит от срока жизни супруга - если «Жена» умирает, ее нельзя держать в живых только потому, что супруг, оказывается, поддерживает указатель на нее. –

+0

@JamesKanze Вот почему есть 'boost :: weak_ptr', я бы предположил. Это позволяет прерывать циклы и безопасное уведомление о мертвых объектах. – cheind

+0

@ ChhristophHeindl Итак, какие указатели должны быть слабыми, а какие нет? 'boost :: weak_ptr' - это взлом, и он обычно не используется. –

7

Это зависит от того, что вы пытаетесь сделать. Во-первых, поскольку прокомментировал, что Kerrek SB имеет , вы не хотите использовать указатели, если применима семантика значения : если Wife можно копировать и присваивать, почти нет причин , чтобы выделить его динамически. В этом случае, однако, я предполагаю, что Wife происходит от Person (хотя, возможно, декоратор для Person бы более целесообразно, так как тот факт, что данный Person Isa Wife может меняться с течением времени), что может быть даже типы производные от Wife (и что Person::current_wife может захотеть провести один из ), и это на самом деле, Wife имеет личность; вам не нужны копии той же жены повсюду.

Если это так, то вам действительно нужно определить протокол для взаимодействия других классов с помощью Wife.Как правило, время жизни Wife не зависит от Person, который удерживает его (хотя, если это декоратор , он может), поэтому Person должен просто удерживать указатель на него, как вы делали . Скорее всего, Wife объект будет иметь различные функции, которые — неявно или явно — контроль ее жизни: вы могли бы иметь что-то вроде:

void Wife::die() 
{ 
    // ... 
    delete this; 
} 

, например. В этом случае, однако, кто бы ни был женат на Wife, должен быть проинформирован , так что current_wife не указывает на мертвого супруг. Обычно для варианта можно использовать какой-либо вариант шаблона наблюдателя. (Обратите внимание, что у вас есть точно такой же вопрос в Java,.. Вы не хотите Person::current_wife, чтобы указать на мертвый Wife Таким образом, вы все еще нужны Wife::die() функцию и образец наблюдателя уведомить супруги)

в подобных случаях (в моем опыте представляют подавляющее большинство динамически выделенных объектов в C++), о той лишь разницей, между C++ и Java является то, что C++ имеет специальный синтаксис для вызова “ деструктор ”; в Java вы используете обычный вызов функции , и вы можете дать функции любое имя, которое вы хотите (хотя dispose кажется широко используемым). Специальный синтаксис позволяет компилятору генерировать дополнительный код для освобождения памяти, но все остальные операции , связанные с окончанием срока службы объекта, по-прежнему должны быть запрограммированы на (в деструкторе в C++ —, хотя в этом случае, может иметь смысл поместить некоторые из них непосредственно в функцию Wife::die ).