0

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

4 回答 4

6

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

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

  1. const

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

  2. fnc1(able x)

    的执行fnc1

  3. const

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

  4. ~able

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

  5. ~able

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

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

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

于 2013-02-24T22:58:22.663 回答
2

在这个函数中:

able fnc1(able x) { ... } 

你是:

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

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

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

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

于 2013-02-24T23:03:03.977 回答
0

这个功能:

able fnc1(able x) { ... } 

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

于 2013-02-24T22:58:42.340 回答
0

听起来您希望将“能”对象的责任转移给调用者。在这种情况下,您将拥有一个类似于 的原型,并且如果调用者不希望其珍贵的东西被弄乱,则由void fn1(able &x)调用者创建该对象的私有副本 。x

正如其他人所说,参数一份,返回值一份。

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

  1. 不得返回对您在此堆栈帧上本地构造的内容(即局部变量或函数参数)的引用
  2. imvho,如果您只是更新调用者引用的内容,那么返回对该对象的引用是没有意义的,并且拥有一个“类似 setter”的 APIvoid作为返回类型同样简单。

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

于 2013-02-24T23:02:33.550 回答