您好,欢迎来到画鸵萌宠网。
搜索
您的当前位置:首页C++程序设计——右值引用

C++程序设计——右值引用

来源:画鸵萌宠网

一、右值引用概念

引用:

        C++98中提出了引用的概念,引用即别名,引用变量与其引用实体共用同一块内存空间,而引用的底层是通过指针来实现,因此使用引用,可以提高程序的可读性。

右值引用(类型&& 右值):

(1)为了提高程序的运行效率,C++11中引入了右值引用;

(2)右值引用也是别名;

(3)只能对右值进行引用。

(4)右值有了名字后,就成了一个普通变量,也就是左值。

二、左值与右值

一般认为:

(3)如果表达式的运行结果是一个临时变量或者对象,认为是右值。

(4)如果表达式运行的结果或单个变量是一个引用,认为是左值。

C++11对右值进行了严格区分:

(1)C语言中的纯右值,比如:10,a+b;

(2)将亡值,比如:表达式的中间结果、函数按照值的方式进行返回。

三、引用与右值引用比较

C++98中的普通引用与const引用,在其引用实体上的区别:

(1)普通引用只能引用左值,不能引用右值;

(2)const引用既可以引用左值,也可以引用右值。

C++ 11中右值引用:

(1)只能引用右值;

(2)一般情况不能直接引用左值。

 (3)右值引用引用左值方法:使用move函数进行转换。

四、值形式返回对象的缺陷

        如果一个类中涉及到资源的管理,用户必须显示的提供拷贝构造函数、赋值运算符重载、析构函数,否则编译器将会自动生成默认的成员函数。而当遇到拷贝对象或对象之间相互赋值的情况时,就会出现一些问题,比如:

class String {
public:
	String(const char* str = "") {
		if (nullptr == str) {
			str = "";
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s) {
		if (this != &s) {
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}
	String operator+(const String& s) {
		char* temp = new char[strlen(_str) + strlen(s._str) + 1];
		strcpy(temp, _str);
		strcpy(temp + strlen(_str), s._str);
		String result(temp);
		delete[] temp;
		return result;
	}

	~String() {
		if (_str) {
			delete[] _str;
			_str = nullptr;
		}
	}

public:
	char* _str;
};

int main() {
	String s1("hello ");
	String s2("world");
	String s3;
	s3 = s1 + s2;
}

        在执行s3 = s1 + s2时,其中operator+是以值形式返回。那么result在返回前,就必须创建一个临时对象,临时对象创建好后,result就被销毁了,然后使用返回的临时对象去赋值s3,赋值完成后,临时对象就被销毁掉。

        可以发现,result、返回的临时对象、s3每个对象创建后,都有自己的空间,且它们存放的内容也相同,相当于创建了三个内容完全相同的对象,对于空间是一种浪费,程序的效率也较低。

优化思路:

        如果可以将result的空间转移给临时对象,临时对象在赋值s3时,再将空间转移给s3,这样就相当于只开辟了一次空间。

实现方法:

        可以发现,result相对于临时对象,临时对象相对于s3来说,都是将亡值,也就是右值。所以我们只需要实现一个移动构造函数和移动赋值函数即可,这样对于右值,则会自动调用移动函数,采用转移的方式进行构造或赋值。

注意:

(1)移动构造和移动赋值函数的参数不能设置为const类型的右值引用,否则资源无法转移导致移动语义失效。

(2)C++11之后,类中默认的成员函数就新添加了移动赋值、移动构造两个成员函数,同理若用户自己没有实现,编译器会默认生成,只不过默认生成的也是采用浅拷贝的方式实现的,所以当类中涉及到资源的管理时,也需要自己显示定义实现。

五、右值引用引用左值

        按照语法定义,右值引用只能引用右值,但有些情况下,也需要用右值去引用左值实现移动语义。

        当需要用右值引用引用左值时,可以通过move函数将左值转化为右值。C++11中,std::move()函数,它并不是什么搬移功能,唯一的功能就是将一个左值强制转化为右值,然后实现移动语义。

应用场景示例:

        所以此时就需要我们使用move函数,显示的告诉编译器将s中的_name、_sno当中右值处理,这样在初始化时就会选中去调用String的移动构造函数。

应用反例:

六、完美转发

1. 概念

        完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。

所谓完美:

        函数模板在向其他函数传递自身参数时,如果相应实参是左值,它就应该被转发为左值;如果相应实参是右值,它就应该被转发为右值。

2. 目的

        保留其他函数对转发来的参数的左右值属性,进行不同的处理。

比如:

        函数func针对参数的左右值属性不同,有不同的处理方式,所以希望函数模板repost在转发参数时,保留其左右值属性,但通过以上写法是不能达到目的的,除法针对左右值属性,编写两个函数模板,但是这又降低了代码的复用性。

        所以希望能够有一种方法,能够实现repost在转发参数时,保留其左右值属性,即完美转发。

3. 实现

(1)将模板函数(包括类模板和函数模板)的参数书写成“T&& 参数名”的形式,这样模板函数既可以接收左值,也可以接收右值引用。(注意:普通函数不行)

(2)C++11提供了模板函数std::forward<T>(参数),用于转发参数。如果参数是左值,转发后仍是左值,如果参数是右值引用,转发后仍是右值引用。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo8.com 版权所有 湘ICP备2023022238号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务