简体   繁体   English

是否会优化未使用的析构函数?

[英]Will unused destructor be optimized out?

Assuming MyClass uses the default destructor (or no destructor), and this code: 假设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;

Is there any optimizer that would be able to remove this loop? 有没有可以消除该循环的优化程序?

Also, is there any way for my code to detect if MyClass is using an empty/default constructor? 另外,我的代码有什么方法可以检测MyClass是否使用空/默认构造函数?

EDIT: Sorry about my horrible code. 编辑:对不起,我的代码很糟糕。 I think this is correct now.. 我认为这是正确的。

The correct syntax for calling a destructor is 调用析构函数的正确语法是

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);

That syntax is valid for types like int (when passed as a template parameter) but is a no-op (does nothing), so template code such as that in std::vector<T> which calls destructors on its contents, is valid. 该语法对于int等类型(作为模板参数传递时)有效,但对no-op无效(不执行任何操作),因此诸如std::vector<T>模板代码(在其内容上调用析构函数)是有效的。

IMO it should be straightforward for the compiler to see that the loop contents contains a no-op, therefore the entire loop itself has no side effects, therefore remove the entire loop. IMO编译器应该很容易看到循环内容包含无操作,因此整个循环本身没有副作用,因此请删除整个循环。 Modern compilers have very sophisticated optimisers and should be more than capable of removing code which has no effect. 现代编译器具有非常复杂的优化器,并且应具有去除无效代码的能力。 If a compiler didn't remove redundant loops, it would emit redundant code in the destructor of a vector<int> ! 如果编译器没有删除冗余循环,它将在vector<int>的析构函数中发出冗余代码! There is no code to emit for the destructor of an int, so there would just be an empty loop iterating through the elements doing nothing. 没有要为int的析构函数发出的代码,因此将只有一个空循环,迭代不执行任何操作的元素。 I'm sure any sane optimiser would remove that entire loop. 我确信任何理智的优化器都会删除整个循环。

Of course, if you're calling the destructor on a class which does do work in its destructor, that must still be called and there will still be a loop (subject to other related optimisations such as unrolling). 当然,如果要在确实在其析构函数中工作的类上调用析构函数,则仍必须调用该析构函数,并且仍将存在循环(取决于其他相关优化,例如展开)。

Another simple example of optimisation based on side-effects is code like this: 基于副作用的优化的另一个简单示例是这样的代码:

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

cout << i;

will probably be optimised to simply print the constant 1000000 with no processing, because the compiler is smart enough to know the overall side effect is i becomes a million and gets printed. 可能会被优化为只打印不经过处理的常数1000000,因为编译器足够聪明,可以知道总体副作用是i变成一百万并被打印。 This is the very basics for some of the impressive things optimisers do, so don't worry about it, it's going to do an excellent job. 这是优化器所做的一些令人印象深刻的事情的基础,因此不必担心,它将做得很好。 If you're ever curious, examine the output assembly in an optimised build to see what's really going on. 如果您有好奇心,请以优化的版本检查输出程序集,以了解实际情况。

There are a few things wrong with this code. 此代码有些错误。

Firstly, you don't need to be calling the destructor. 首先,您不需要调用析构函数。 MyClass buffer* = new MyClass[i]; delete[] buffer; does that just fine. 这样就好了吗 (Note, not the array syntax.) (注意,不是数组语法。)

That said, you comment leads me to believe you meant something else, like: 就是说,您的评论使我相信您的意思还包括:

// 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

Then later: 然后再:

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

Of course there's no need to delete anything, since we used a vector. 当然,由于我们使用了向量,因此无需删除任何内容。 This is the correct syntax for calling a destructor. 这是调用析构函数的正确语法。

Will it be optimized? 会优化吗? It depends of the compiler can determine if the destructor does nothing. 它取决于编译器能否确定析构函数是否不执行任何操作。 If the destructor is compiler-generated, I'm sure it'll remove the worthless loop. 如果析构函数是由编译器生成的,那么我确定它将消除无用的循环。 If the destructor is user-defined but in the header, it'll also be able to see it does nothing and remove the loop. 如果析构函数是用户定义的,但在标头中,则它也可以看到它不执行任何操作并删除循环。

However, if it's in some other object file, I don't think it will, even if it's empty. 但是,如果它在其他目标文件中,即使它为空,我也认为不会。 That depends on your compilers ability to optimize during the linking phase. 这取决于您的编译器在链接阶段进行优化的能力。 The best way to know is to look at the generated assembly. 最好的了解方法是查看生成的程序集。

You don't create a dynamic array as you have done above. 您不会像上面那样创建动态数组。 You do it as follows: 您可以按照以下步骤进行操作:

MyClass* buffer = new MyClass[i];

That aside the loop you have above doesn't call the destructor either. 除了上面的循环外,它也不会调用析构函数。 If the class has an overloaded "~" operator then it will call that code. 如果该类具有重载的“〜”运算符,则它将调用该代码。

So no ... no compiler will optimise that loop out. 因此,没有……没有编译器会优化该循环。 The code is highly unlikely to compile as well. 该代码也极不可能编译。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM