vc9 和 gcc 之间的不同析构函数行为

Posted

技术标签:

【中文标题】vc9 和 gcc 之间的不同析构函数行为【英文标题】:Different destructor behavior between vc9 and gcc 【发布时间】:2009-08-23 18:06:39 【问题描述】:

以下代码在 GCC 和 vc9 上编译时给出了不同数量的析构函数。 AFAIK 在 vc9 上运行时显示 5 个析构函数,我理解。调用 + 重载运算符,并创建两个对象,返回时创建一个临时对象。这使得销毁 3 个对象成为可能。当调用重载 = 运算符时,会创建一个对象,并在返回时再次创建一个临时对象。这总共有五个破坏,不包括在 main 开始时创建的三个对象。

但是当我在 GCC 上编译时,我得到 3。

这让我猜想在函数终止并返回时没有创建临时对象?或关于编译器之间不同行为的问题。我就是不知道,如果能澄清一下就好了。

#include <iostream>
using namespace std;

class planetCord 
    double x, y, z;
public:
    planetCord()  x = y = z = 0; 
    planetCord(double j, double i, double k)  x = j; y = i; z = k;     
    ~planetCord()  cout << "destructing\n"; 
    planetCord operator+(planetCord obj);
    planetCord operator=(planetCord obj);
    void show();
;

planetCord planetCord::operator +(planetCord obj) 
    planetCord temp;
    temp.x = x + obj.x;
    temp.y = y + obj.y;
    temp.z = z + obj.z;
    return temp;


planetCord planetCord::operator =(planetCord obj) 
    x = obj.x;
    y = obj.y;
    z = obj.z;
    return *this;


void planetCord::show() 
    cout << "x cordinates: " << x << "\n";
    cout << "y cordinates: " << y << "\n";
    cout << "z cordinates: " << z << "\n\n";


int main() 
    planetCord jupiter(10, 20, 30);
    planetCord saturn(50, 100, 200);
    planetCord somewhereDark;

    jupiter.show();
    saturn.show();
    somewhereDark.show();
    somewhereDark = jupiter + saturn;  
    jupiter.show();
    saturn.show();
    somewhereDark.show();
    return 0;

【问题讨论】:

【参考方案1】:

GCC 正在实施“返回值优化”以跳过临时对象。将 VC9 设置为 Release 模式,它可能也会这样做。

如果 GCC 真的很好,它会看到 temp 内部 operator+ 将被默认初始化,就像 somewhereDark 一样,如果它试图内联函数,则可以直接使用对 somewhereDark 的引用.或者它看到传值是无用的,可以改为传引用。

【讨论】:

vc9 在设置为释放模式时产生 4 个析构函数。我尝试对项目进行不同的优化,但无法产生相同的结果。 尝试使用初始化列表而不是在构造函数中赋值并从析构函数中删除代码。我也会为您的运营商使用 const&,这很卫生。【参考方案2】:

C++ 编译器允许但非强制的优化是转紧序列:

ctor for new temporary object X
copy ctor from X to other object Y
dtor for X

直接在 Y 上执行 ctor。一个非常好的 C++ 优化器可以跨函数 clal 执行此操作(即,当 X 是函数的返回值时)。看起来 gcc 优化得更好。当您使用两个编译器的优化选项时,结果是否会发生变化?

【讨论】:

我无法更改结果,除非我将 Visual Studio 设置为发布模式。尝试设置不同的优化并使用设置,但它保持不变。【参考方案3】:

您的代码有很多问题。我可以建议您研究两个概念 - 常量和引用。如果您的 C++ 教科书没有涵盖这些内容,请购买一本新教科书 - 我强烈推荐 Accelerated C++ 作者:Koenig & Moo。

【讨论】:

已经打算读完现在正在读的那本。听说我现在的教科书介绍了不好的做法,所以我被推荐了你提到的那本。 如果您当前的灵感来自上面的代码,请不要完成它。这不仅不好,而且明显是错误的。【参考方案4】:

实际上,在 GCC 中,正在制作临时文件。它们是:

在运算符+中。 由 operator+ 返回。 由 operator= 返回。

在 MSVC(我认为;无法测试)中,也正在制作临时文件。然而,有些并没有像 GCC 那样被优化掉。它们是:

作为 operator+ 的参数。 在运算符+中。 由 operator+ 返回。 作为 operator= 的参数。 由 operator= 返回。

具有讽刺意味的是,我认为 MSVC 就在这里,因为我不确定 GCC 的行为是否标准。

要使它们的行为相同,请使用const 引用而不是按值传递对象。

【讨论】:

这是一个勇敢的声称 GCC 不是标准的。您认为哪些优化会导致 GCC 不符合标准? @York,我不太了解标准。我只是/怀疑/这可能不是标准所说的应该(可能?)。 @Stranger:如果您对标准的了解不够透彻,那么您肯定没有足够的知识来提出这个要求。我怀疑 GCC 是合规的。 我说“我认为”和“我不确定”。我认为这是足够的免责声明。 =X

以上是关于vc9 和 gcc 之间的不同析构函数行为的主要内容,如果未能解决你的问题,请参考以下文章

虚拟析构函数和未定义的行为

1构造函数和析构函数为什么没有返回值?

为啥这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次

为啥在 C++ 中第二次调用析构函数未定义的行为?

析构函数调用线

这个构造函数和析构函数发生了啥?