【C++11】左值引用,右值引用,移动/复制构造,完美转发

左值与右值

字面意思是可以放在等号左边的就是左值,只能放在等号右边的就是右值(为何是"可以""只能"?例如++i是左值,但他依然可以放在等号右边)。

严格上的定义:可以取地址的就是左值,反之为右值(不具名)。

左值引用

使用方法:在类型后加& 。

只能引用左值,或加const关键字来引用右值,但不能修改,此情况与使用引用的目的相违背(使用引用的目的就是为了修改),所以一般不用。

右值引用

C++11新引入,只能引用右值。

使用方法:在类型后加&&,如T && 。

作用:延长右值生命周期,减少对象的复制,提升性能。

cpp 复制代码
//右值引用
class X {
public:
	X() {
		cout << "X()" << endl;
	}

	X(const X& x) {
		cout << "X(const X& x)" << endl;
	}

	~X() {
		cout << "~X()" << endl;
	}
};

X makeX() {
	X x1;
	return x1; 
}

int main() {
	X&& x2 = makeX();
	return 0;
}

以上代码中,调用makeX(),如果不是右值引用需要发生两次拷贝,三次构造,如果用右值引用,makeX()返回的将亡值延长生命周期,只发生一次拷贝,提升性能。但目前很多编译器都已经对此进行了返回值优化,所以在大部分场景无需刻意纠结。

复制构造器与移动构造器

复制构造器形参是一个左值引用。

移动构造器接受一个右值,没有了复制构造中的内存复制。

移动复制构造器的风险:如果一个对象移动到另一个对象时发生异常,造成目标对象不完整,后果无法预测(所以移动构造需要加noexcept关键字)。

cpp 复制代码
class MyString {
public:
	MyString() :str(nullptr), len(0) {}

	MyString(const char* ch) :str(nullptr), len(0) {
		if (ch != nullptr) {
			len = strlen(ch);
			str = new char[len + 1];
			strcpy(str, ch);
			cout << "Constructor" << endl;
		}
	}

	//拷贝构造函数
	MyString(const MyString& other) :str(nullptr), len(0) {
		if (other.str != nullptr) {
			len = other.len;
			str = new char[len + 1];
			strcpy(str, other.str);
			cout << "Copy Constructor" << endl;
		}
	}

	//移动构造函数
	MyString(MyString&& other) :str(nullptr), len(0) {
		str = other.str;
		len = other.len;
		other.str = nullptr;
		other.len = 0;
		cout << "Move Constructor" << endl;
	}

	~MyString() {
		if (str != nullptr) {
			delete[] str;
			str = nullptr;
			len = 0;
		}
	}

private:
	char* str;
	size_t len;
};

int main() {
	MyString string1("Test");
	MyString string2(string1);
	MyString string3(move(string1));
	return 0;
}

注:使用msvc编译以上代码的时候ide可能会出现如下的错误:

error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

vs中在"项目右键-->属性-->C/C++ -->预处理器-->预处理器定义"中添加上"_CRT_SECURE_NO_WARNINGS"即可。

以上代码中move()的作用:将对象的状态或所有权从一个对象转移到另一个对象,将左值引用转化为右值引用,继而可通过右值引用使用该值,以用于移动语义。

万能引用

形如T&&或auto &&并且发生类型推导的引用,包括模板和auto,如:

cpp 复制代码
	//万能引用
	template <class T>
	T test(T&& t){}
cpp 复制代码
auto&& x = get_val();

完美转发

特性:在函数内部完美地转发函数实参原始类型和值类型,也就是说,如果函数传入的实参是左值引用,转发时保持实参的左值引用属性和左值引用类型。

完美转发的原理:基于引用折叠,即,实际类型和模板类型只要有左值引用参与进来,最后推导的结果就是一个左值引用(遇左则左)。

两种实现方式:

(1)使用static_cast:

cpp 复制代码
static_cast<T &&>(t);

(2)使用标准库中forward()方法:

cpp 复制代码
forward<T>(t);

注:forward()与move()的区别:move()将一个实参转换为右值引用,并且move()不需要模板实参。

相关推荐
AI+程序员在路上1 小时前
VS Code 完全使用指南:下载、安装、核心功能与 内置AI 编程助手实战
开发语言·人工智能·windows·开源
invicinble1 小时前
这里对java的知识体系做一个全域的介绍
java·开发语言·python
catchadmin1 小时前
使用 PHP TrueAsync 改造 Laravel 协程异步化的可行路径
开发语言·php·laravel
wbs_scy1 小时前
【Linux 线程进阶】进程 vs 线程资源划分 + 线程控制全详解
java·开发语言
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题】【Java基础篇】第15题:JDK1.7中HashMap扩容为什么会发生死循环?如何解决
java·开发语言·数据结构·后端·面试·哈希算法
郑州光合科技余经理3 小时前
同城O2O海外版二次开发实战:从支付网关到配送算法
开发语言·前端·后端·算法·架构·uni-app·php
南子北游4 小时前
Python学习(基础语法1)
开发语言·python·学习
张健11564096484 小时前
使用信号量限制并发数量
开发语言·c++
jc06204 小时前
6.1云原生之Docker
c++·docker·云原生
糯米团子7494 小时前
Web Worker
开发语言·前端·javascript