栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++11:移动语义

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++11:移动语义

移动语义 1、产生背景

背景:在程序运行的过程中,会产生大量的临时对象。临时对象只在某一个表达式执行的过程中创建,表达式执行完毕时,临时对象马上就被销毁。其作用是用来进行过渡的,造成了资源拷贝的不必要的浪费。

需求:希望将临时对象直接转移到新对象中

问题:临时对象是右值,只能通过 const 引用来绑定,但const引用同时又可以绑定到左值。C++11之前,当传递参数时,const引用是无法判断传递过来的参数是左值还是右值(所以const引用又称万能引用)。需要在语法层能够唯一确定传递过来的参数是右值。

解决:C++11 右值引用,并增加移动语义函数。

2、左值、右值
  • 左值:可以取地址的表达式,

  • 右值:不能取地址的表达式,临时对象、匿名对象、临时变量、字面值常量都是右值

3、右值引用

右值引用:只能绑定到右值不能绑定到左值。右值引用有名字的是左值,无名字的是右值。也可以说作为函数返回值的 && 是右值,声明出来的 && 是左值,即引用在等号左边是左值,在右边是右值。

int && ref = 10; //正确
int && ref = a; //error

std::move

添加编译选项: -fno-elide-constructors

唯一功能:强制将左值转换为右值,等同于 static_cast(lvaule),没有性能上的提升。将左值转换为右值后,左值就不能直接使用了。

std::move()作用于内置类型没有任何作用,内置类型本身是左值还是右值,经过移动后不会改变,对于自定义类型效果不错。

4、移动语义函数

移动语义可以将资源通过浅拷贝方式从一个对象转移到另一个对象,只是转移,没有内存拷贝,这样能够减少不必要的临时对象的创建、拷贝以及销毁。

传递右值,优先调用移动语义的函数,即移动语义函数的执行优先于复制控制语义函数的执行。

移动构造函数

  • 浅拷贝
  • 转以后将其置为 nullptr
// 移动构造函数:右值引用作为函数参数
// 如果使用右值引用,意味着被修改,因为右值马上就要被销毁
String(String &&rhs)
: _pstr(rhs._pstr) { // 1、浅拷贝
    // 2、转移后,将其设为nullptr
    // 防止临时对象被销毁时,把它所拥有的资源回收了,不能达到转移资源的目的
    rhs._pstr = nullptr;
    cout << "String(String &&)" << endl;
}

移动赋值运算符函数

  • 自移动
  • 释放左操作数
  • 浅拷贝
  • 返回 this 指针
// 移动赋值运算符函数
String &operator=(String &&rhs) {
    // rhs绑定的是一个右值,但rhs本身在函数内部是左值(因为其有名字)
    cout << "String &operator=(String &&)" << endl;
    //1、自移动
    if(this != &rhs) {
        //2、释放左操作数
        delete [] _pstr; 
        _pstr = nullptr;
        //3、浅拷贝
        _pstr = rhs._pstr; 
        rhs._pstr = nullptr;
    }
    //4、返回*this
    return *this;
}

实现内置类型 string 类

#include 

#include 
#include 
using std::cout;
using std::endl;
using std::vector;

class String {
public:
	String()
	: _pstr(new char[1]())
	{	cout << "String()" << endl;	}

	
	String(const char * pstr)
	: _pstr(new char[strlen(pstr) + 1]()) {
		strcpy(_pstr, pstr);
		cout << "String(const char*)" << endl;
	}

	// 拷贝构造函数:const万能引用,无法分辨左值和右值
	String(const String & rhs)
	: _pstr(new char[strlen(rhs._pstr) + 1]()) {
		strcpy(_pstr, rhs._pstr);
		cout << "String(const String &)" << endl;
	}
	
    // 赋值运算符函数
	String & operator=(const String & rhs) {
		cout << "String & operator=(const String&)" << endl;	
		if(this != &rhs) {
			delete [] _pstr;
			_pstr = new char[strlen(rhs._pstr) + 1]();
			strcpy(_pstr, rhs._pstr);
		}
		return *this;
	}

	// 移动构造函数
	String(String && rhs)
	: _pstr(rhs._pstr)  {   
		rhs._pstr = nullptr;
		cout << "String(String&&)" << endl;
	}
    
	// 移动赋值运算符函数
	String & operator=(String && rhs) {	
		cout << "String& operator=(String&&)" << endl;
		if(this != &rhs) {
			delete [] _pstr;
			_pstr = rhs._pstr;
			rhs._pstr = nullptr;
		}
		return *this;
	}

	~String() {
		if(_pstr){
			delete [] _pstr;
			_pstr = nullptr;
		}
		cout << "~String()" << endl;
	}

	friend std::ostream & operator<<(std::ostream &os, const String&);
private:
	char * _pstr;
};

std::ostream & operator<<(std::ostream &os, const String& rhs) {
	os << rhs._pstr;
	return os;
}
5、forward 转发

参数在传递过程中保持其值属性的功能,即在参数传递过程中,左值传递后仍是左值,右值仍是右值,根据参数的类型转发给正确的函数。

6、emplace_back

就地构造对象,只调用一次构造函数,避免了内存的拷贝和移动。

push_back(); // 两次构造(临时对象创建,容器内部创建),一次析构(临时对象析构)。
emplace_back(); // 一次构造(容器内直接创建)
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1033453.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号