2013-06-10 1 views
3

Предположим, что есть вектор Item sЧто произойдет, если элемент std :: vector «совершит самоубийство» (используя delete this;)?

vector<Item*> items; //{item1, item2, item3} 

Затем в другой части кода,

items[1]->suicide(); 

где функция suicide является:

void Item::suicide() 
{ 
    delete this; 
} 

Что items вектор размера и как это сейчас? Это ОК для этого?

Edit (могу я задать дополнительный вопрос?): Если желаемое расположение выходе {item1, item3}, размер 2, и нет оборванного указателя, как это делается в самоуничтожающемся способе (с item2 сам)?

Редактировать 2: Спасибо за все ответы! Потрясающие. Поэтому я, наконец, решил и нашел способ сделать это из-за пределов объекта, потому что это была плохая практика и излишне сложная

+1

См. Связанные вопросы с тегом [самоуничтожение]: http://stackoverflow.com/questions/tagged/self-destruction%20c%2b%2b?sort=votes&pagesize=50 –

+0

приятный, не знал о что тег – stijn

+1

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

ответ

6

Что такое размер вектора товаров и как он находится сейчас? Такой же. Вызов функции не изменяет содержимое вектора и размер вообще. Он просто освобождает память, на которую указывает указатель.

Это нормально? Точнее: это законный C++? Да. Это хорошее программирование стиля? Нет. Позвольте мне подробно остановиться на последнем:

  • Должно быть разделение проблем: кто отвечает за управление памятью? Контейнер или пользователь класса Item или сам класс Item?

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

  • Каков способ сделать это? Управление памятью в современном и безопасном коде C++ в основном осуществляется с использованием интеллектуальных указателей, таких как std::shared_ptr и std::unique_ptr, и контейнеров, таких как std::vector и std::map.

  • Если класс Item является типом значения (это означает, что вы можете просто скопировать его и не иметь полиморфного поведения в терминах виртуальных функций), то просто используйте std::vector<Item> вместо этого для вашего кода. Деструкторы будут вызываться автоматически, как только элемент будет удален из контейнера. Контейнер делает это за вас.

  • Если класс Item имеет полиморфное поведение и может быть использован в качестве базового класса, а затем использовать std::vector<std::unique_ptr<Item>> или std::vector<std::shrared_ptr<Item>> вместо этого и предпочитает std::unique_ptr решения, потому что он добавляет меньше накладные расходы. Как только вы перестанете ссылаться на объект, он автоматически удаляется деструктором используемого интеллектуального указателя. Вам (почти) больше не нужно беспокоиться о утечке памяти.

  • Единственный способ, с помощью которого вы можете произвести утечку памяти, состоит в том, что у вас есть объекты, содержащие std::shared_ptrs, которые обращаются друг к другу циклически. Использование std::unique_ptrs может предотвратить эту проблему. Другой выход - std::weak_ptrs.

Итог: Не предоставлять функции suicide(). Вместо этого ответственность возлагается исключительно на код вызова. Для управления памятью используйте стандартные библиотеки и интеллектуальные указатели.

Редактировать: Что касается вопроса в вашем редактировании. Просто напишите

items.erase(items.begin() + 1); 

Это будет работать для всех типов: std::vector значений или указателей. Вы можете найти хорошую документацию от std::vector и стандартной библиотеки C++ here.

1

Ваша функция suicide не имеет никакого отношения к вектору Items, не говоря уже об этом. Итак, с точки зрения вектора: при вызове функции ничего не меняется, и это нормально.

+0

Из вектора POV объект с удаленным указателем теперь находится не в правильном состоянии: он больше не является назначаемым. Вектор находится в специальном, почти недопустимом состоянии. (Но вы все равно можете его уничтожить, и вы все равно можете вернуть его в правильное состояние.) – curiousguy

+0

, что не имеет смысла. Вектор _ все еще находится в одном и том же состоянии. Не специальный, недействительный. Пойдите, проверьте это. Макет памяти не изменился. И указатель всегда назначается. И у вектора нет никакого реального дела с состоянием его элементов. Я не сторонник OP, должен сделать это, но downvoting мой ответ является слишком большим мостом, поскольку в нем нет ложных утверждений. – stijn

+0

«Вектор все еще находится в том же состоянии». «Нет, вектор сломан (но исправляется), потому что теперь он содержит недопустимые объекты. «И указатель всегда присваивается.» «Нет, указатель недействителен; все, что вы можете сделать, это присвоить ** это ** действительное значение. «Но ничто не мешает моему ответу - это мост слишком далеко». Я не согласен с вашим ответом. «Поскольку он не содержит ложных утверждений», я утверждаю, что это так. – curiousguy

1

Указатель станет недействительным, вот и все. Вы должны быть осторожны, чтобы не до delete его снова. vector<Item*> НЕ удалит элементы самостоятельно.

+0

Чтобы быть конкретным, он удалит память, в которой хранятся * указатели *, но она не удалит память, на которую указывают указатели. – Dan

+0

@ Dan: Я верю вы написали это наоборот: 'delete' освободит указанную память, но не сам указатель (т. он оставит контейнер нетронутым) –

+0

Возможно, я был неясен, я имел в виду, что вектор очистит его элементы (указатели) от его разрушения, но не там, где они указывают. – Dan

0

Вектор не имеет понятия, что вы делаете в другом месте в коде, поэтому он сохранит dangling pointer оригиналу Item.

«Все в порядке, сделайте это?»

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

+0

.. если вы хотите заменить его другим предметом – stijn

+0

@stijn: «Заменить» хорошо вписывается в «Настроить» –

+0

ах, семантика:] вы правы, хотя я неправильно понял его как «скорректируйте размер вектора» или так – stijn

0

Это нормально в случае вектора указателей, поскольку вектор не будет вызывать деструктор предмета. Но вы должны как-то знать, какие указатели все еще действительны.

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

5

Член самоубийства не меняет вектор. Таким образом, вектор содержит элемент, который является недопустимым указателем, и формально вы не можете много сделать с недопустимым указателем, даже копируя или сравнивая его с неопределенным поведением. Таким образом, все, что к нему обращается, включает изменение размера вектора, является UB.

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

+0

Доступ к оборванному указателю не UB; разыгрывающий один есть. –

+1

@OliCharlesworth, мне нужна ссылка, где поведение определено или, по крайней мере, осталось для реализации. (Я никогда не нашел его, но, возможно, его пропустил, особенно если он был введен в C++ 11; загрузка недопустимого дескриптора сегмента - это, безусловно, ловушка на x86, и такая вещь упоминается как обоснование многочисленного времени UBness обсуждался на Usenet). – AProgrammer

+0

Загрузка его в произвольный регистр? –

0

Wow, Кажется, вы делаете ошибку ввода.Он должен быть vector<Item *> Items; Что касается Вашего вопроса:

  1. размера векторных предметов не меняется, значит, что он все еще имеет три указателей на объекты Item.
  2. содержание вектора не изменяется: до предметов [1] -> самоубийство(), предметы [0] = 0x000001, элементы [1] = 0x000005, элементы [2] = 0x000009 после предметов [1] -> суицид(), Элементы [0] = 0x000001, Элементы [1] = 0x000005, Элементы [2] = 0x000009
  3. Это определенно ОККАЯ.

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

Код Items[1]->sucide() только возвращает память, удерживаемую или направленную указателем Элементы [1] в систему, она ничего не будет делать с самим указателем. Элементы [1] все еще имеют одно и то же значение, но указывают на небезопасную область ,

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

class MustOnHeap 
{ 
    private: 
     ~MustOnHeap() { // ...} 
    public: 
     void suicide() { delete this;} 

}; 

Затем класс не может иметь никакого экземпляра, который добавлен в стек, поскольку деструктор является закрытым, а компилятор должен организовать вызов деструктора, когда объект выходит из области действия. Для этого вы должны выделить их в кучу, MustOnHeap* p = new MustOnHeap;, а затем уничтожить его явно: p-> suicide();

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