复制构造函数调用两次

时间:2013-02-24 22:55:35

标签: c++

编译器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

4 个答案:

答案 0 :(得分:6)

您正在通过值将able对象传递给函数,然后按值返回它。其中每个都涉及一个副本,并将使用您的复制构造函数。首先,将其复制到fnc1(*x);的函数中。然后使用return x;将该副本复制出函数。

关于输出的顺序,您目击的是:

  1. const

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

  2. fnc1(able x)

    执行fnc1

  3. const

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

  4. ~able

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

  5. ~able

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

  6. return x;引起的第二个副本可能被编译器遗漏(即使它有一些副作用):

      

    在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象(函数或catch子句参数除外)的名称时,具有与函数相同的cv-unqualified类型返回类型,通过将自动对象直接构造到函数的返回值

    中,可以省略复制/移动操作

答案 1 :(得分:2)

在此功能中:

able fnc1(able x) { ... } 

你是:

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

  2. 按值返回类型able 的对象:这意味着将构造一个临时的,它是您要返回的对象的副本,尽管这最后一次调用编译器可以在(Named) Return Value Optimization或(N)RVO下省略复制构造函数(然后到析构函数)。这就是复制构造函数的第二次调用的原因。

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

    另请注意,您的程序泄漏内存。您正在使用new分配对象,并且您永远不会通过对delete的相应调用来解除分配。

答案 2 :(得分:0)

此功能:

able fnc1(able x) { ... } 

将要求复制对象 - 当您使用没有引用的类时,这是合同的一部分。这允许fnc1()与对象“混乱”并且传入的原始对象保持不变 - 这有时正是您想要的。

答案 3 :(得分:0)

听起来你想要“能够”的责任。对象转移到调用者。在这种情况下,您有void fn1(able &x)之类的原型,如果它不想要,则由调用者创建x对象的私有副本它的精确度会被搞砸。

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

只要从函数返回 new object ,就需要构造函数来构建该对象。如果你不想在那一点上有一个新对象,那么你的函数应该返回一个指针或对另一个对象的引用。然而,

  1. 一定不能返回你在这个堆栈框架上本地构建的东西的引用(即局部变量或函数参数)
  2. imvho,如果您只是更新调用者引用的内容,那么返回对该对象的引用是没有意义的,并且拥有&#34; setter-like&#同样简单34;您将void作为返回类型的API。
  3. 我可以看到没有关注#2的唯一原因是你想要链接函数,即你比其他编程模式重视obj->fn1()->fn2()->fn3()。在这种情况下,我建议您接收并将指针返回able个对象,并接受fnx()使用obj->m而不是obj.m的事实。 {{1}}访问成员。