未使用的析构函数会被优化吗?

时间:2010-04-13 14:38:58

标签: c++ optimization g++

假设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 个答案:

答案 0 :(得分:3)

调用析构函数的正确语法是

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的总体副作用变为百万并被打印。这是优化者所做的一些令人印象深刻的事情的基础知识,所以不要担心,它会做得很好。如果您一直很好奇,请检查优化版本中的输出程序集,看看实际发生了什么。

答案 1 :(得分:3)

此代码存在一些问题。

首先,您不需要调用析构函数。 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)

当然没有必要删除任何内容,因为我们使用了一个向量。这是调用析构函数的正确语法。

会优化吗?这取决于编译器可以确定析构函数是否什么都不做。如果析构函数是编译器生成的,我相信它会删除无用的循环。如果析构函数是用户定义的但是在标题中,它也能够看到它什么都不做并删除循环。

但是,如果它在某个其他目标文件中,我认为它不会,即使它是空的。这取决于编译器在链接阶段进行优化的能力。最好的方法是查看生成的程序集。

答案 2 :(得分:0)

您不像上面那样创建动态数组。你这样做:

MyClass* buffer = new MyClass[i];

除了你上面的循环外,它也不会调用析构函数。如果类有一个重载的“〜”运算符,那么它将调用该代码。

所以没有...没有编译器会优化该循环。代码也不太可能编译。