复制构造函数调用了两次
copy constructor called twice
编译器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;
将该副本复制到函数之外。
至于你的输出顺序,你看到的是:
-
const
对象被复制-这是作为参数传递到函数中的对象。
-
fnc1(able x)
fnc1
的执行。 -
const
对象被再次复制-这是从函数返回的对象。
-
~able
Destructor被调用-这是在传递被销毁的参数时创建的副本,因为您已经到达函数范围的末尾。
-
~able
Destructor被调用-这是在
fnc1(*x);
行完成时从被销毁的函数返回的临时对象。
由return x;
引起的第二个副本可能会被编译器消除(即使它有一些副作用(:
在具有类返回类型的函数中的返回语句中,当表达式是与函数返回类型具有相同cv不合格类型的非易失性自动对象(函数或catch子句参数除外(的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作
在此函数中:
able fnc1(able x) { ... }
您是:
按值获取输入参数。这意味着将创建其副本以初始化
x
。这就是第一次调用复制构造函数的原因。按值返回
able
类型的对象:这意味着将构造一个临时对象,它是您要返回的对象的副本,尽管编译器可能会在(命名(返回值优化或(N(RVO下取消对复制构造函数(当然还有对析构函数(的最后一次调用。这就是第二次调用复制构造函数的原因。
因此,您看到的复制构造函数的两个调用。
还要注意,您的程序会泄漏内存。您正在使用new
分配对象,并且从未通过对delete
的相应调用来解除分配。
此函数:
able fnc1(able x) { ... }
将要求对对象进行复制-当您在没有引用的情况下使用类时,这是约定的一部分。这允许fnc1(("搅乱"对象,并且传入的原始对象保持不变——这有时正是您想要的。
听起来您希望将"able"对象的责任转移到调用者身上。在这种情况下,您会有一个像void fn1(able &x)
这样的原型,如果调用方不希望x
对象的珍贵性被破坏,那么它就由调用方创建一个私有副本。
正如其他人所说,一个参数副本,一个返回值副本。
只要从函数返回一个新对象,就需要一个构造函数来构建该对象。如果此时不需要新对象,那么函数应该返回指向其他对象的指针或引用。然而,
- 不能返回对此堆栈框架上本地构建的对象(即本地变量或函数参数(的引用
- imvho,如果您只是更新调用方引用的内容,那么返回对该对象的引用是没有意义的,并且在返回类型为
void
的情况下使用"类似于setter的"API也同样简单
我能看到的不遵循#2的唯一原因是您想要链接函数,即您将obj->fn1()->fn2()->fn3()
的值置于其他编程模式之上。在这种情况下,我建议您接收并返回指向able
对象的指针,并接受fnx()
使用obj->m
而不是obj.m
来访问成员的事实。
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- C++析构函数调用两次,堆栈分配的复合对象
- 对于优化级别为 0 的 std::vector,析构函数被调用两次
- 为什么转换运算符调用复制构造函数两次,而等效函数只调用它一次
- 调用一个小函数两次(例如在if条件和主体中)比将结果存储在局部变量中更可取
- 为什么这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次
- 调用某个回调函数两次会导致分段错误:Nan
- 重载运算符 new(),为什么构造函数被调用两次?
- 为同一存储位置调用构造函数两次是否合法?
- 为什么在下面的代码中调用复制构造函数两次
- 为什么在这里调用析构函数两次
- while 循环在一个函数调用中执行两次
- 为什么这个构造函数被调用两次
- 析构函数在与 STL 的共享指针中调用两次
- 为什么在C 中超载邮政增量运算符两次调用构造函数
- 现代C++编译器是否能够避免在某些条件下两次调用常量函数
- 构造函数在不同线程中的静态单例类上调用两次
- 类析构函数在创建shared_ptr时调用两次
- 在c++中,使用指针的两个函数调用之间的区别是什么?
- 用define替换两次函数调用