拷贝(复制)构造、移动构造与返回值优化

本文深入探讨C++中拷贝构造、移动构造函数的概念及其优化策略,包括移动语义、右值引用与编译器优化如返回值优化(RVO)的原理与应用。通过具体代码示例,解析如何避免不必要的深拷贝,提高程序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

拷贝构造函数

拷贝构造函数(又称复制构造函数),用来创建已存在对象的副本。用已有对象去初始化另一个对象

还有拷贝赋值运算符,当需要显示地声明拷贝构造函数时,一般建议同时声明拷贝赋值运算符。

如果不声明拷贝构造函数(或拷贝赋值运算符),编译器将会生一个默认的拷贝构造函数(或拷贝赋值运算符)(当被调用时生成)。

注意:区分赋值和拷贝构造,也就是区分赋值与初始化。

class A {
	int n;
public:
	A(int n) :n{ n } { cout << "ctor" << endl; }
	A(A& b) {
		n = b.n;
		cout << "Copy ctor" << endl;
	}
	A& operator = (const A& b) {
		n = b.n;
		cout << "=" << endl;
		return *this;
	}
};
int main() {
	A a = 1;//调用的是ctor,而不是拷贝构造
	A c{ 1 };//普通构造函数,输出ctor
	A b{ a };//拷贝构造,输出Copy ctor
	b = a; //赋值,输出=
	return 0;
}

移动语义(移动构造、移动赋值)

C++11引用了移动语义,可以减少深拷贝的次数,提升效率。移动语义是与右值引用息息相关的。

右值引用

简单来说,可以取地址的是左值,不能取地址的是右值。
左值引用 &, 右值引用 &&

class A {
public:
	A() {};    
	
    // 移动构造函数
	A(A && a) {
		cout << "move" << endl;
	};
};
int main() {
	vector<A> v;
	v.push_back(A());//A()是个临时对象,右值,所以调用move移动构造函数

补充: move()函数

move()可将左值变成右值

编译器优化

class TextFile {
public:
	TextFile() {};
	TextFile(const TextFile& tf) {
		cout << "copy" << endl;
	};
	TextFile& operator=(TextFile& tf) {
		cout << "operator=" << endl;
		return *this;
	};
};
 
TextFile get_tmp_text_file() {
	TextFile tf;
	return tf;
}
 
int main() {
 
	TextFile tf = get_tmp_text_file();

这是很经典的例子,很多文章都会讲 TextFile tf = get_tmp_text_file(); 会调用两次拷贝构造函数,一次是 get_tmp_text_file 函数返回时,将函数返回拷贝到临时变量里,第二次是在 main 函数内构造 TextFile 对象时,将临时变量拷贝到 tf 对象上。所以,程序会输出两遍 copy。

然而,如果你现在写了这么一个程序,真的跑一遍,它什么都不会输出。

不要怀疑自己,代码没错,教程也没错,这是因为GCC的 返回值优化 。

当一个函数返回一个对象实例,一个临时对象将被创建并通过复制构造函数把目标对象复制给这个临时对象。C++标准允许省略这些复制构造函数,即使这导致程序的不同行为,即使编译器把两个对象视作同一个具有副作用。

这是C++标准允许编译器独立实现的优化。被称为返回值优化(RVO/NRVO)。

关于RVO/NRVO历史可以参见维基百科 Return value optimization 词条

G++编译时,会默认对代码进行返回值优化,优化后的等价代码如下:

TextFile get_tmp_text_file(TextFile * tf) {
	// 直接在tf上构造
}

int main() {
    TextFile tf;
	get_tmp_text_file(&tf);
}

因此就不需要再调用 TextFile 对象的拷贝构造函数。

要关闭这种优化,只要在编译时加上 -fno-elide-constructors 强制G++总是使用拷贝构造函数。

g++ 2DimensionArray.cpp -o run -fno-elide-constructors && ./run

输出:

copy
copy
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值