编译器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
答案 0 :(得分:6)
您正在通过值将able
对象传递给函数,然后按值返回它。其中每个都涉及一个副本,并将使用您的复制构造函数。首先,将其复制到fnc1(*x);
的函数中。然后使用return x;
将该副本复制出函数。
关于输出的顺序,您目击的是:
const
复制对象 - 这是作为参数传递给函数的对象。
fnc1(able x)
执行fnc1
。
const
再次复制对象 - 这是从函数返回的对象。
~able
调用析构函数 - 这是在传递被销毁的参数时创建的副本,因为您已到达函数范围的末尾。
~able
调用析构函数 - 这是在行fnc1(*x);
完成时从被销毁的函数返回的临时对象。
由return x;
引起的第二个副本可能被编译器遗漏(即使它有一些副作用):
在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象(函数或catch子句参数除外)的名称时,具有与函数相同的cv-unqualified类型返回类型,通过将自动对象直接构造到函数的返回值
中,可以省略复制/移动操作
答案 1 :(得分:2)
在此功能中:
able fnc1(able x) { ... }
你是:
按值输入参数 。这意味着将创建它的副本以初始化x
。这就是第一次调用复制构造函数的原因。
按值返回类型able
的对象:这意味着将构造一个临时的,它是您要返回的对象的副本,尽管这最后一次调用编译器可以在(Named) Return Value Optimization或(N)RVO下省略复制构造函数(然后到析构函数)。这就是复制构造函数的第二次调用的原因。
因此,两个调用您正在看到的复制构造函数。
另请注意,您的程序泄漏内存。您正在使用new
分配对象,并且您永远不会通过对delete
的相应调用来解除分配。
答案 2 :(得分:0)
此功能:
able fnc1(able x) { ... }
将要求复制对象 - 当您使用没有引用的类时,这是合同的一部分。这允许fnc1()与对象“混乱”并且传入的原始对象保持不变 - 这有时正是您想要的。
答案 3 :(得分:0)
听起来你想要“能够”的责任。对象转移到调用者。在这种情况下,您有void fn1(able &x)
之类的原型,如果它不想要,则由调用者创建x
对象的私有副本它的精确度会被搞砸。
参数的一个副本,返回值的一个副本,正如其他人所说的那样。
只要从函数返回 new object ,就需要构造函数来构建该对象。如果你不想在那一点上有一个新对象,那么你的函数应该返回一个指针或对另一个对象的引用。然而,
void
作为返回类型的API。我可以看到没有关注#2的唯一原因是你想要链接函数,即你比其他编程模式重视obj->fn1()->fn2()->fn3()
。在这种情况下,我建议您接收并将指针返回able
个对象,并接受fnx()
使用obj->m
而不是obj.m
的事实。 {{1}}访问成员。