явный деструктор - PullRequest
       6

явный деструктор

3 голосов
/ 04 февраля 2011

Следующий код используется только для иллюстрации моего вопроса.

template<class T>
class array<T>
{
 public:
    // constructor
    array(cap = 10):capacity(cap)
    {element = new T [capacity]; size =0;}

    // destructor
    ~array(){delete [] element;}
    void erase(int i);
    private:
      T *element;
      int capacity;
      int size;
};



template<class T>
void class array<T>::erase(int i){
     // copy
     // destruct object
    element[i].~T();  ////
    // other codes
} 

Если у меня есть array<string> arr в main.cpp.Когда я использую erase(5), объект element[5] уничтожается, но пространство element[5] не освобождается, могу ли я просто использовать element[5] = "abc", чтобы ввести новое значение здесь?или мне нужно использовать размещение new, чтобы поместить новое значение в пространство element [5]?

Когда программа завершится, arr<string> вызовет свой собственный деструктор, который также вызовет delete [] element.Таким образом, деструктор string будет запускаться, чтобы сначала уничтожить объект, а затем освободить пространство.Но так как я явно уничтожил element[5], имеет ли значение, что деструктор (который вызывается деструктором arr) запускается дважды, чтобы уничтожить element[5]?Я знаю, что пространство нельзя освободить дважды, как насчет объекта?Я сделал несколько тестов и обнаружил, что это нормально, если я просто уничтожаю объект дважды вместо освобождения места дважды.

Обновление

Ответы:

(1)I have to use placement new if I explicitly call destructor.

(2) repeatedly destructing object is defined as undefined behavior which may be accepted in most systems but should try to avoid this practice.

Ответы [ 2 ]

6 голосов
/ 04 февраля 2011

Вам необходимо использовать новый синтаксис размещения:

new (element + 5) string("abc");

Было бы неверно сказать element[5] = "abc"; это вызовет operator= на element[5], который не является допустимым объектом, приводящим к неопределенному поведению.

Когда программа заканчивается, arr<string> вызывает свой собственный деструктор, который также вызывает delete [] element.

Это неправильно: в конечном итоге вы вызовете деструктор для объектов, деструкторы которых уже были вызваны (например, elements[5] в вышеупомянутом примере). Это также приводит к неопределенному поведению.

Рассмотрите возможность использования std::allocator и его интерфейса. Это позволяет легко отделить выделение от строительства. Используется контейнерами стандартной библиотеки C ++.

2 голосов
/ 04 февраля 2011

Просто чтобы объяснить, как именно UD может вас укусить ...

Если вы erase() элемент - явно вызываете деструктор - этот деструктор может делать такие вещи, как уменьшение счетчиков ссылок + очистка, удаление указателей и т. Д. Когда ваш деструктор массива <> затем делает delete[] element, это вызовет деструкторы для каждого элемента по очереди, и для стертых элементов эти деструкторы могут повторить обслуживание счетчика ссылок, удаление указателя и т. д., но на этот раз исходное состояние не соответствует ожидаемому, и их действия могут привести к сбою программы.

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

Простейший тип T, иллюстрирующий эту проблему:

struct T
{
    T() : p_(new int) { }
    ~T() { delete p_; }
    int* p_;
};

Здесь значение p_, установленное new, будет удалено во время erase(), и если оно не изменится при запуске ~array(). Чтобы исправить это, p_ должен быть изменен на что-то, для чего delete действителен до ~array() - либо каким-либо образом очистив его до 0, либо до другого указателя, возвращенного new. Наиболее разумным способом сделать это является размещение new, которое создаст новый объект, получив новое и действительное значение для p_, перезаписав старое и бесполезное содержимое памяти.

Тем не менее, вы можете подумать, что можете создавать типы, для которых повторный деструктор безопасен: например, установив p_ в 0 после delete. Это, вероятно, будет работать на большинстве систем, но я уверен, что в Стандарте есть что-то, что дважды вызывает деструктор - это UD независимо от того,

...