复制构造函数调用了两次

copy constructor called twice

本文关键字:两次 函数调用 复制      更新时间:2023-10-16

编译器gcc 4.5.3(cygwin(

我试图确定在什么条件下为参数调用复制构造函数,我想找到一种方法来传递一个不需要调用复制构造函数的参数。我构建了以下测试代码来探讨这个问题。

在下面的代码中,为fnc1((调用了两次复制构造函数。为什么要多次调用它?

有没有办法不调用复制构造函数?

# include <iostream>
using namespace std;
class able {
public:
   long x;
   able(): x(1) {}
   able(const able&) {cout << " const "; }
   ~able() { cout << " ~able" << endl; }
};
able fnc1(able x)         { cout << "fnc1(able x)"         ; return x; }
able fnc2(able& x)        { cout << "fnc2(able& x)"        ; return x; }
able fnc3(const able&  x) { cout << "fnc3(const able&  x)" ; return x; }
able fnc4(able const & x) { cout << "fnc4(able const & x)" ; return x; }
able fnc5(able* x)        { cout << "fnc4(able* x)"        ; return *x; }
int main(int argc, char** argv) {
   able* x = new able();
   fnc1(*x);
   fnc2(*x);
   fnc3(*x);
   fnc4(*x);
   fnc5(x);
   cout << "test fini" << endl;
   return 0;
}
output
 const fnc1(able x) const  ~able
  |                 |      |
  |                 |      o first destrucor
  |                 |      
  |                 o second call
  o first call
 ~able
 |
 o second destructor
fnc2(able& x) const  ~able
fnc3(const able&  x) const  ~able
fnc4(able const & x) const  ~able
fnc4(able* x) const  ~able
test fini

您按值将able对象传递到函数中,然后按值返回。每一个都涉及一个副本,并将使用您的副本构造函数。首先,它被复制到具有fnc1(*x);的函数中。然后用return x;将该副本复制到函数之外。

至于你的输出顺序,你看到的是:

  1. const

    对象被复制-这是作为参数传递到函数中的对象。

  2. fnc1(able x)

    fnc1的执行。

  3. const

    对象被再次复制-这是从函数返回的对象。

  4. ~able

    Destructor被调用-这是在传递被销毁的参数时创建的副本,因为您已经到达函数范围的末尾。

  5. ~able

    Destructor被调用-这是在fnc1(*x);行完成时从被销毁的函数返回的临时对象。

return x;引起的第二个副本可能会被编译器消除(即使它有一些副作用(:

在具有类返回类型的函数中的返回语句中,当表达式是与函数返回类型具有相同cv不合格类型的非易失性自动对象(函数或catch子句参数除外(的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作

在此函数中:

able fnc1(able x) { ... } 

您是:

  1. 按值获取输入参数。这意味着将创建其副本以初始化x。这就是第一次调用复制构造函数的原因。

  2. 按值返回able类型的对象:这意味着将构造一个临时对象,它是您要返回的对象的副本,尽管编译器可能会在(命名(返回值优化或(N(RVO下取消对复制构造函数(当然还有对析构函数(的最后一次调用。这就是第二次调用复制构造函数的原因。

因此,您看到的复制构造函数的两个调用。

还要注意,您的程序会泄漏内存。您正在使用new分配对象,并且从未通过对delete的相应调用来解除分配。

此函数:

able fnc1(able x) { ... } 

将要求对对象进行复制-当您在没有引用的情况下使用类时,这是约定的一部分。这允许fnc1(("搅乱"对象,并且传入的原始对象保持不变——这有时正是您想要的。

听起来您希望将"able"对象的责任转移到调用者身上。在这种情况下,您会有一个像void fn1(able &x)这样的原型,如果调用方不希望x对象的珍贵性被破坏,那么它就由调用方创建一个私有副本。

正如其他人所说,一个参数副本,一个返回值副本。

只要从函数返回一个新对象,就需要一个构造函数来构建该对象。如果此时不需要新对象,那么函数应该返回指向其他对象的指针或引用。然而,

  1. 不能返回对此堆栈框架上本地构建的对象(即本地变量或函数参数(的引用
  2. imvho,如果您只是更新调用方引用的内容,那么返回对该对象的引用是没有意义的,并且在返回类型为void的情况下使用"类似于setter的"API也同样简单

我能看到的不遵循#2的唯一原因是您想要链接函数,即您将obj->fn1()->fn2()->fn3()的值置于其他编程模式之上。在这种情况下,我建议您接收并返回指向able对象的指针,并接受fnx()使用obj->m而不是obj.m来访问成员的事实。