Будет ли оптимизирован неиспользованный деструктор? - PullRequest
0 голосов
/ 13 апреля 2010

Предполагается, что MyClass использует деструктор по умолчанию (или не имеет деструктора), и этот код:

MyClass *buffer = new MyClass[i];
// Construct N objects using placement new
for(size_t i = 0; i < N; i++){
    buffer[i].~MyClass();
}
delete[] buffer;

Есть ли оптимизатор, который мог бы удалить этот цикл?

Кроме того, может ли мой код определить, использует ли MyClass пустой / конструктор по умолчанию?

РЕДАКТИРОВАТЬ: Извините за мой ужасный код. Я думаю, что теперь это правильно ..

Ответы [ 3 ]

3 голосов
/ 13 апреля 2010

В этом коде есть несколько ошибок.

Во-первых, вам не нужно вызывать деструктор. MyClass buffer* = new MyClass[i]; delete[] buffer; делает это просто отлично. (Обратите внимание, не синтаксис массива.)

Тем не менее, ваш комментарий приводит меня к мысли, что вы имели в виду что-то еще, например:

// vector, because raw memory allocation is bad
std::vector<char> memory(sizeof(MyClass) * count); 

std::vector<MyClass*> objs; objs.reserve(count);
for (size_t i = 0; i < count; ++i)
    objs.push_back(new (memory[sizeof(MyClass) * i]) MyClass()); // place it

Потом позже:

for (size_t i = 0; i < count; ++i)
    objs[i].~MyClass(); // destruct (note syntax)

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

Будет ли это оптимизировано? Это зависит от компилятора, который может определить, что деструктор ничего не делает. Если деструктор генерируется компилятором, я уверен, что он удалит бесполезный цикл. Если деструктор определен пользователем, но находится в заголовке, он также сможет увидеть, что он ничего не делает, и удалить цикл.

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

3 голосов
/ 13 апреля 2010

Правильный синтаксис для вызова деструктора:

template<typename T>
void destruct(T& x)
{
    x.~T();  // call destructor on x
}

// The below is valid even though ints do not have destructors
int x;
destruct(x);

Этот синтаксис действителен для типов, таких как int (при передаче в качестве параметра шаблона), но он не используется (ничего не делает), поэтому допустим код шаблона, такой как в std::vector<T>, который вызывает деструкторы для своего содержимого.

IMO для компилятора должно быть просто увидеть, что содержимое цикла содержит запрет, поэтому весь цикл не имеет побочных эффектов, поэтому удалите весь цикл. Современные компиляторы имеют очень сложные оптимизаторы и должны быть более чем способны удалить код, который не имеет никакого эффекта. Если компилятор не не удаляет избыточные циклы, он будет генерировать избыточный код в деструкторе vector<int>! Для деструктора типа int не существует кода, который нужно выдать, поэтому будет просто пустой цикл, повторяющий элементы, которые ничего не делают. Я уверен, что любой здравомыслящий оптимизатор удалит весь этот цикл.

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

Другим простым примером оптимизации на основе побочных эффектов является код, подобный следующему:

for (int i = 0; i < 1000000; ++i)
    ;  // just count up i, no statement (same as no-op)

cout << i;

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

0 голосов
/ 13 апреля 2010

Вы не создаете динамический массив, как вы делали выше. Вы делаете это следующим образом:

MyClass* buffer = new MyClass[i];

То, что у вас есть вышеупомянутый цикл, также не вызывает деструктор. Если класс имеет перегруженный оператор «~», он будет вызывать этот код.

Так что нет ... никакой компилятор не оптимизирует этот цикл. Код также вряд ли будет компилироваться.

...