2016-07-15 3 views
1

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

struct Foo{ 
    struct Bar { virtual int calc(int x) = 0; }; 
    Bar* barX; 
    Bar* barY; 
    int x,y; 
    Foo(Bar* bx,Bar* by) : barX(by),barY(by) {} 
    void process(int xx,int yy){ 
     x = barX->calc(xx); 
     y = barY->calc(yy); 
    } 
    ~Foo(); 
}; 

struct IDBar : Foo::Bar { int calc(int x) { return x; } }; 

int main() { 
    IDBar* b = new IDBar(); 
    Foo f = Foo(b,b); 
    Foo f2 = Foo(new IDBar(),new IDBar()); 
} 

Я не могу использовать C++ 11 (т.е. нет smartpointers), и я не уверен на 100% ... Это правильный способ удалить оба (или, возможно, только один) из Bar объектов:

Foo::~Foo(){ 
    if (barX == barY){ delete barX; } 
    else { delete barX; delete barY; } 
} 

?

PS: Идея состоит в том, что Foo владеет объектами Bar (таким образом, они будут нести ответственность за их удаление). Bar s, переданные конструктору, не должны использоваться ни для чего другого. На самом деле Bar может принадлежать только одному Foo (недостаток, который я понял только позже, но на данный момент это нормально). Более того, Foo не должен быть скопирован (возможно, я должен это явно запретить).

+2

Кому принадлежит Бары? –

+0

Как насчет импульсных интеллектуальных указателей? – KIIV

+0

@ KarolyHorvath Foo владеет барами. – user463035818

ответ

1

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

Это правда, однако, что это склонность к ошибкам, и мы хотели бы поймать подобные ошибки или предотвратить их. Если бы был какой-то способ гарантировать, что никто не считает, что они владеют ресурсом, когда Foo делает, мы будем в пути. В C++ 11 это легко с std::unique_ptr. В более ранних версиях C++ существует std::auto_ptr. Хотя более ранние стандарты C++ не имеют конструкторов перемещения, std::auto_ptr функционирует аналогичным образом, отказываясь от ресурса от старого std::auto_ptr при назначении или копировании на новый std::auto_ptr.

Я рекомендую вам использовать std::auto_ptr.

Если ситуация, когда один и тот же ресурс явно разрешен для обоих параметров конструктора для класса Foo, создайте новый конструктор, который принимает только один std::auto_ptr<Bar>.

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

+0

@ tobi303: Понятно, ты изменил свой любимый ответ. Но это не помогает, если вы хотите предложить вариант использования, когда пользователь может одновременно работать с двумя объектами Foo (a, b) и Foo (b, c)! – Roland

+0

@ Роланд Я понимаю, что вы имеете в виду. Тем не менее, этот случай использования не может быть поддержан, так как я думаю, что намерение искателя заключается в том, что 'Foo' берет эксклюзивное право собственности на переданный указатель. Передача' b' на два 'Foo' означает, что у него есть два владельца. –

1

нет нет нет нет нет. god no

Foo::~Foo(){ 
     if (barX == barY){ delete barX; } 
     else { delete barX; delete barY; } 
    } 

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

int main() { 
    IDBar* b = new IDBar(); 
    IDBar* b2 = new IDBar(); 
    IDBar* b3 = new IDBar(); 
    Foo f = Foo(b,b); 
    Foo f2 = Foo(b2,b3); 
    delete b; 
    delete b2; 
    delete b3; 
} 

Идеальный пример управления памятью объектов, который сам по себе является LinkedList. LinkedList создает узлы внутри себя. Пользователь не знает о них, а вместо этого просто вставляет T. LinkedList удалит их, когда он будет удален. Пользователь только отвечает за удаление LinkedList, потому что он или она создали это.

std::list<MyObject*> list; 
MyObject* object = new MyObject; 
list.push_back(object); 
//The list isn't going to delete object because it doesn't own it 
delete object; 
+2

вообще я немного аллергия на запись во всех кепках и даже, кроме этого, я действительно не согласен с вашим" NO NO NO ... ". Собственность берется 'Foo', когда я передаю' Bar' в конструктор, поэтому 'Foo' должен удалить их, а не кого-либо еще. – user463035818

+0

Тогда почему Foo не владеет им? – Taztingo

+0

, потому что пользователь должен выбрать какой-то «бар» для создания экземпляра – user463035818

1

Если вы настаиваете на своем строительстве и не можете прислушиваться к правилу проектирования Тазтинго; то ответ «да»: это правильный путь!

А затем создайте прочную документацию API класса Foo и его конструктор, чтобы взять на себя ответственность за всю жизнь объекта Bar.

+0

Мне нравится этот ответ. – Taztingo

+0

Я мог прислушаться к этому «правилу», но я просто не понимаю его. – user463035818

+0

@ tobi303: Tazingo делает встречный пример, что вы не можете одновременно создавать два объекта Foo (a, b) и Foo (b, c), поэтому требуется сильное описание API. – Roland

1

Я бы сказал, что сравнения далеки от совершенства - представьте себе, что у вас есть три указатели.

Для обеспечения двойного delete никогда не возникает, как насчет того, чтобы вы звоните delete только один? Рассмотрим сценарий:

  1. Выделите пул памяти (допустим, достаточно выделить тысячу объектов). Это должно быть выделено на кучу с помощью обычного вызова new, где-то внутри нового класса PoolAllocator<T> и delete в его дескрипторе.
  2. Реализовать пул, чтобы он поддерживал функцию void* getMemory<T>(), возвращающую память для T, чтобы использовать и продвигать внутренний указатель.
  3. Удостоверьтесь, что все, что вызывается T, может быть создано только с помощью места размещения нового в пуле, предоставленном в память.

В конце концов, вам не нужно беспокоиться о памяти - объект пула может быть обычной переменной стека с внутренними элементами в куче.

В противном случае, что запрещает вам внедрять собственные прото-умные указатели?

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