代码之家  ›  专栏  ›  技术社区  ›  Brendan Long

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

  •  0
  • Brendan Long  · 技术社区  · 15 年前

    假设 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;
    

    是否有任何优化器可以删除此循环?

    还有,我的代码有没有办法检测 类名 是否使用空/默认构造函数?

    编辑:对不起,我的代码太可怕了。我想这是正确的……

    3 回复  |  直到 15 年前
        1
  •  3
  •   GManNickG    15 年前

    这个代码有一些问题。

    首先,您不需要调用析构函数。 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
  •  3
  •   AshleysBrain    15 年前

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

    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(作为模板参数传递时)有效,但不是op(不执行任何操作),因此模板代码(如 std::vector<T> 对其内容调用析构函数的方法是有效的。

    IMO编译器应该很容易看到循环内容包含no op,因此整个循环本身没有副作用,因此移除整个循环。现代编译器有非常复杂的优化程序,应该能够删除没有效果的代码。如果编译器 没有 删除多余的循环,它将在 vector<int> !对于int的析构函数没有要发出的代码,因此只会有一个空循环在元素之间循环,不做任何事情。我相信任何理智的乐观主义者都会消除这个循环。

    当然,如果您在一个在析构函数中确实有效的类上调用析构函数,那么它仍然必须被调用,并且仍然会有一个循环(受制于其他相关的优化,如展开)。

    另一个简单的基于副作用的优化示例是这样的代码:

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

    可能会被优化为只打印常量1000000而不进行处理,因为编译器足够聪明,可以知道总体的副作用是 i 变成百万,然后打印出来。这是乐观主义者所做的一些令人印象深刻的事情的基础,所以不要担心,它会做得很好。如果你很好奇,可以在一个优化的构建中检查输出组件,看看到底发生了什么。

        3
  •  0
  •   Goz    15 年前

    您不会像上面所做的那样创建动态数组。您可以这样做:

    MyClass* buffer = new MyClass[i];
    

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

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