can a C++;函数返回一个带有构造函数和析构函数的对象 我试图确定C++函数是否返回具有构造函数和析构函数的对象是安全的。我对该标准的理解是,它应该是可能的,但我用简单的例子进行的测试表明,它可能存在问题。例如,以下程序: #include <iostream> using namespace std; struct My { My() { cout << "My constructor " << endl; } ~My() { cout << "My destructor " << endl; } }; My function() { My my; cout << "My function" << endl; return my; } int main() { My my = function(); return 0; }

can a C++;函数返回一个带有构造函数和析构函数的对象 我试图确定C++函数是否返回具有构造函数和析构函数的对象是安全的。我对该标准的理解是,它应该是可能的,但我用简单的例子进行的测试表明,它可能存在问题。例如,以下程序: #include <iostream> using namespace std; struct My { My() { cout << "My constructor " << endl; } ~My() { cout << "My destructor " << endl; } }; My function() { My my; cout << "My function" << endl; return my; } int main() { My my = function(); return 0; },c++,constructor,destructor,C++,Constructor,Destructor,在MSVC++上编译时,但在使用gcc编译时,会给出以下输出: My constructor My function My destructor 这是一种“未定义的行为”,还是其中一个编译器的行为不符合标准?如果是后者,哪一个?gcc输出更接近我的预期 到目前为止,我一直在设计我的类,假设每个构造函数调用最多有一个析构函数调用,但是这个例子似乎表明这个假设并不总是成立的,并且可能依赖于编译器。标准中是否有什么规定了这里应该发生什么,或者最好避免函数返回非平凡对象?如果此问题重复,则表示歉意。在

在MSVC++上编译时,但在使用gcc编译时,会给出以下输出:

My constructor
My function
My destructor
这是一种“未定义的行为”,还是其中一个编译器的行为不符合标准?如果是后者,哪一个?gcc输出更接近我的预期


到目前为止,我一直在设计我的类,假设每个构造函数调用最多有一个析构函数调用,但是这个例子似乎表明这个假设并不总是成立的,并且可能依赖于编译器。标准中是否有什么规定了这里应该发生什么,或者最好避免函数返回非平凡对象?如果此问题重复,则表示歉意。

在这两种情况下,编译器都会为您生成一个副本构造函数,该构造函数没有输出,因此您不知道是否调用了它:

在第一种情况下,使用编译器生成的复制构造函数,该构造函数与第二个析构函数调用相匹配。行
返回my
调用复制构造函数,为其提供用于构造返回值的变量
my
。这不会产生任何输出

my
随后被销毁。函数调用完成后,返回值在
{function();
行的末尾被销毁

在第二种情况下,返回的副本是完整的(编译器可以作为优化来执行此操作)。您只有一个
My
实例。(是的,它可以执行此操作,即使它更改了程序的可观察行为!)

虽然一般来说,如果你定义了自己的构造函数和析构函数,你也应该定义自己的复制构造函数(和赋值操作符,如果你有c++11的话,还可以定义移动构造函数和移动赋值)

尝试添加您自己的复制构造函数,看看您得到了什么

My (const My& otherMy) { cout << "My copy constructor\n"; }

My(const My&otherMy){cout问题是您的类
My
违反了;如果您编写了自定义析构函数,那么您还应该编写自定义复制构造函数(和复制赋值运算符,但这与此无关)

与:

如您所见,(复制)构造函数与析构函数正确匹配


gcc下的输出保持不变,因为gcc正在执行标准允许的(但不是必需的)复制省略。

这里缺少两件事:复制构造函数和NRVO

在MSVC++中看到的行为是“正常”行为;
my
被创建,函数的其余部分被运行;然后,当返回时,创建对象的副本。本地
my
对象被销毁,副本被返回给调用方,调用方只是丢弃它,导致其销毁

为什么您似乎缺少构造函数调用?因为编译器自动生成了一个副本构造函数,它被调用但不打印任何内容。如果您添加了自己的副本构造函数:

My(const My& Right) { cout << "My copy constructor " << endl; }
到目前为止,我一直在设计我的类,假设每个构造函数调用最多有一个析构函数调用[…]

您仍然可以“假设”,因为这是真的。每个构造函数调用都将同时执行一个析构函数调用。(请记住,如果您自己处理空闲/堆内存中的内容。)

[…]并且可以依赖于编译器[…]

在这种情况下,它不能。这取决于优化。如果应用优化,MSVC和GCC的行为相同

你为什么看不到相同的行为? 1.您不会跟踪对象发生的一切。编译器生成的函数会绕过您的输出。 如果您想“跟进”编译器对对象所做的事情,您应该定义所有特殊成员,这样您就可以真正跟踪所有内容,并且不会被任何隐式函数绕过

struct My
{  
   My() { cout << "My constructor " << endl; }
   My(My const&) { cout << "My copy-constructor " << endl; }
   My(My &&) { cout << "My move-constructor " << endl; }
   My& operator=(My const&) { cout << "My copy-assignment " << endl; }
   My& operator=(My &&) { cout << "My move-assignment " << endl; }
  ~My() { cout << "My destructor " << endl; }
};
这里我们有我上面提到的“完全非优化行为”,我将参考我在这里画的要点

A constructor (see 2.) function (see 3.) B copy from A (see 4.) A destructor (see 5.) C copy from B (see 6.) B destructor (see 7.) C destructor (instance in main, destroy at end of main) 一个构造函数(参见2。) 功能(见3。) B从A复制(见4) 析构函数(见5。) C从B处复制(见6) B析构函数(见7.)
C析构函数(实例在main中,销毁在main的末尾)为什么要将函数设置为静态?
My
被分配到函数的堆栈上。当您返回时,它会从堆栈中弹出并被销毁。您需要使用关键字new在堆上创建它,以便从函数中返回它,而不是让它被销毁。@crush:no,函数返回一个
My
对象,因此对象应该在销毁之前进行复制。您所说的内容适用于返回指向局部变量的指针或引用。@MatteoItalia它将返回一个副本,而不是一个引用…因此它们不是同一个对象。函数中声明的对象在函数退出时总是会销毁。我想我误解了不过,作者是在追求。(以为他在问它为什么会破坏)@crush:对不起,我想我误解了你的意思。:)谢谢,我现在明白了“问题”是静默复制构造函数,编译器依赖性是由于允许的,但不是强制性的优化
My(const My& Right) { cout << "My copy constructor " << endl; }
My constructor      <----+
My function              |      this is the local "my" object
My copy constructor   <--|--+
My destructor       <----+  |   this is the return value
My destructor         <-----+
My constructor  <-- called at the beginning of f
My function    
My destructor   <-- called after f is terminated, since
                    the caller discarded the return value of f
struct My
{  
   My() { cout << "My constructor " << endl; }
   My(My const&) { cout << "My copy-constructor " << endl; }
   My(My &&) { cout << "My move-constructor " << endl; }
   My& operator=(My const&) { cout << "My copy-assignment " << endl; }
   My& operator=(My &&) { cout << "My move-assignment " << endl; }
  ~My() { cout << "My destructor " << endl; }
};
#include <iostream>
using namespace std;
struct A
{  
   A(void) { cout << "A constructor " << endl; }
  ~A(void) { cout << "A destructor " << endl; }
};
struct B
{  
   B(A const&) { cout << "B copy from A" << endl; }
  ~B(void) { cout << "B destructor " << endl; }
};

struct C
{
   C(B const &) { cout << "C copy from B" << endl; }
  ~C(void) { cout << "C destructor " << endl; }
};

B function() { A my; cout << "function" << endl; return my; }

int main()
{ 
  C my_in_main(function());
  return 0;
}
A constructor (see 2.) function (see 3.) B copy from A (see 4.) A destructor (see 5.) C copy from B (see 6.) B destructor (see 7.) C destructor (instance in main, destroy at end of main)