更新1:按建议添加“打印”。
更新2:拆分为多个文件,尝试阻止gcc优化。
更新3:记录的复制构造函数并输入添加函数。
更新4:在主要内容中添加了Clang和第二个cout的输出。
我希望将参数析构函数作为函数中的最后一个语句调用。从此以后,我希望以下代码输出以下内容。
default constructor: 008DFCF8
other constructor: 008DFCEC
copy constructor: 008DFBC0
in member add
destroying: 008DFBC0
copy constructor: 008DFBB8
copy constructor: 008DFBB4
in function add
destroying: 008DFBB4
destroying: 008DFBB8
3 == 3
end of main
destroying: 008DFCEC
destroying: 008DFCF8
使用MSVC(Visual Studio)时,输出符合预期。但GCC(4.8.2-19ubuntu1)输出以下内容,表明函数参数的析构函数是在main()中的第一个cout语句之后但在最后一个之前调用的。
default constructor: 0x7fff2fcea510
other constructor: 0x7fff2fcea520
copy constructor: 0x7fff2fcea550
in member add
copy constructor: 0x7fff2fcea540
copy constructor: 0x7fff2fcea530
in function add
3 == 3
destroying: 0x7fff2fcea530
destroying: 0x7fff2fcea540
destroying: 0x7fff2fcea550
end of main
destroying: 0x7fff2fcea520
destroying: 0x7fff2fcea510
对于那些很奇怪clang ++(3.4-1ubuntu3)输出的人。
default constructor: 0x7fff52cf9878
other constructor: 0x7fff52cf9870
copy constructor: 0x7fff52cf9860
copy constructor: 0x7fff52cf9858
in function add
3 == copy constructor: 0x7fff52cf9850
in member add
3
destroying: 0x7fff52cf9850
destroying: 0x7fff52cf9858
destroying: 0x7fff52cf9860
end of main
destroying: 0x7fff52cf9870
destroying: 0x7fff52cf9878
问题:
// Test.h
#ifndef __TEST_H__
#include <iostream>
using namespace std;
class Test
{
public:
int val;
Test(Test const &a) : val(a.val)
{
cout << "copy constructor: " << this << endl;
}
Test() : val(1)
{
cout << "default constructor: " << this << endl;
}
Test(int val) : val(val)
{
cout << "other constructor: " << this << endl;
}
~Test()
{
cout << "destroying: " << this << endl;
}
int add(Test b);
};
#endif
// Add.cpp
#include "Test.h"
int Test::add(Test b)
{
cout << "in member add" << endl;
return val + b.val;
}
int add(Test a, Test b)
{
cout << "in function add" << endl;
return a.val + b.val;
}
// Main.cpp
#include "Test.h"
int add(Test a, Test b);
int main()
{
Test one, two(2);
cout << add(one, two) << " == " << one.add(two) << endl;
cout << "end of main" << endl;
return 0;
}
使用以下代码编制GCC:
g++ -c Add.cpp -o Add.o ; g++ -c Main.cpp -o Main.o ; g++ Add.o Main.o -o test
答案 0 :(得分:2)
在调用“add(a,b)”时,以及在调用成员add(b)时创建临时对象。 我认为在gcc的情况下你看到的是当这些函数返回时,add()函数(参数)中的局部变量被销毁。 最后两行“完成”行用于变量“一”和“两”。
VC是不同的 - 但这没有错,它只是表明两个编译器正在以不同的方式优化代码。
不要只打印“完成”,还要尝试打印“this”值。在构造函数中打印“this”,然后你可以看到构造函数和析构函数调用是如何配对的。
哎呀 - 我在VC和GCC之间加了一点。 VC首先打印“完成”三次 - 可能是因为add()参数被销毁,而GCC将它们全部打开,可能是因为它内联了添加函数。
答案 1 :(得分:2)
考虑这一行:
cout << add(one, two) << " == " << one.add(two) << endl;
写作:
cout << add(one, two);
cout << " == " << one.add(two) << endl;
这会改变GCC的打印输出吗?
或那样:
auto i = add(one, two);
cout << i << " == ";
auto j = one.add(two)
cout << j << endl;
我认为这是关于副作用(不是关于内联)。
VC似乎更早地调度副作用(破坏临时对象),而GCC在语句结束时安排它 - ;
临时对象生存期
在各种情况下创建临时对象:绑定a 引用prvalue,从函数返回prvalue,强制转换为a prvalue,抛出异常,进入异常处理程序,然后进入 一些初始化。 在每种情况下,所有临时工作都被摧毁 as 评估全表达的最后一步(词法) 包含创建它们的点,如果是多个 临时的创造,他们按照相反的顺序被摧毁 创造的顺序。即使评估结束,也是如此 抛出异常。
在我看来,这说明GCC和VC(特别是在破坏后打印“3 == 3”,这对我来说很奇怪)。
答案 2 :(得分:2)
似乎C ++标准在确定何时必须调用函数参数析构函数时可能有点模棱两可。 C ++ 03和C ++ 11都在5.2.2 / 4“函数调用”(强调添加)中说:
参数的生命周期在其所在的函数结束时结束 定义的回报。每个参数的初始化和销毁 发生在调用函数的上下文中。
因此参数的析构函数在概念上不会在函数的右括号中出现。这是我不知道的事情。
该标准给出了一个说明,解释了这意味着如果参数的析构函数抛出,那么只考虑调用函数或“更高”的异常处理程序(具体来说,即使被调用函数具有'函数 - 尝试 - 阻止',它不被考虑。)
虽然我认为目的是针对MSVC的行为,但我可以看到某人如何解释允许GCC行为的阅读。
然后,也许这是海湾合作委员会的一个错误?