移动语义

完成所有权的移交,当拷贝构造和赋值构造时,目标对象的所有权必须移交给我们的新的对象,原始对象将丧失所有权,_p指针将不再指向原来的那个数组;

左值与右值

C原始定义

  • 左值:可以出现在赋值号的左边或者右边
  • 右值:只能出现在赋值号的右边

C++的定义

  • 左值:用于标识非临时对象或者非成员函数的表达式
  • 右值:用于标识临时对象的表达式或与任何对象无关的值(纯右值),或用于标识即将失效的对象的表达式(失效值)

左值引用与右值引用

左值引用:&

右值引用:&&

  • 深拷贝需要频繁分配和释放内存,效率比较低
  • 移动语义的目的:所有权移交,不需要重新构造和析构
  • 为与构造函数兼容,移动语义必须为引用,而不能是指针或者普通量
  • 普通引用传递左值,以允许函数内部修改目标数据对象
  • 为区分左值引用,实现移动语义时必须传递右值引用
  • 为保证能够修改目标数据对象,在函数内部必须将右值引用作为左值引用对待
class A
{
public:
A() :_n(), _p(nullptr){}
explicit A(int n):_n(n),_p(new int[n]){}
A(int n, int *p) :_n(n), _p(p) {}
A(A && that);
A & operator=(A && that);
virtual ~A() { if (_p) { delete[]_p, _p = nullptr; } }
public:
int & operator[](int i);
const int & operator[](int i)const;
private:
int _n;
int *_p;
}; A::A(A && that)
{
//nullptr:C++11预定义的空指针类型nullptr_t的常对象
//可隐式转换为任意指针类型和bool类型,但不能转化为整数类型,以取代NULL
_n = that._n, _p = that._p, that._n = , that._p = nullptr;
//*this = that;//此代码不会调用下面重载的赋值操作符函数
//具名右值引用that在函数内部被当作左值,不是右值
//匿名右值引用才会被当作右值,理论上如此.....
//*this = static_cast<A &&>(that);//等价于*this = std::move(that);
//上一行代码可以调用下面重载的移动赋值操作符,但是有可能导致程序崩溃
//因为this指向的本对象可能刚刚分配内存,_p字段所指向的目标数据对象无定义
} A & A::operator=(A && that)
{
if (_p)//删除此行代码可能导致内存泄漏
delete[]_p;
//可以测试是否为同一个对象,以避免自身复制操作,但意义不大
_n = that._n, _p = that._p, that._n = , that._p = nullptr;
return *this;
}

移动语义重载

class A
{
public:
A() :_n(), _p(nullptr) {}
explicit A(int n) :_n(n), _p(new int[n]) {}
A(int n, int *p) :_n(n), _p(p) {}
//可以同时提供拷贝语义与移动语义版本,前者使用常左值引用
//不能修改目标数据对象的值,后者则可以修改
A(const A & that);
A(A && that);
A & operator =(const A & that);//深拷贝版本
A & operator =(A && that);//移动赋值版本
virtual ~A() { if (_p) { delete[]_p, _p = nullptr; } }
public:
int & operator[](int i);
const int & operator[](int i)const;
private:
int _n;
int *_p;
}; int main()
{
A a();
for (int i = ; i < ; i++)
{
a[i] = i + ;
}
A b(a);//调用拷贝构造函数
b = a; //调用普通赋值版本
//把左值引用转换为右值引用,否则会调用左值版本
A c(static_cast<A &&>(a));//调用移动构造版本
c = static_cast<A &&>(a);//调用移动赋值版本
return ;
}

左值引用同样可以实现移动语义

class A
{
public:
A() :_n(), _p(nullptr) {}
explicit A(int n) :_n(n), _p(new int[n]) {}
A(int n, int *p) :_n(n), _p(p) {}
A(A & that);//重载非常量版本;移动构造语义
A(const A & that);//重载常量版本;深拷贝构造语义
A & operator=(A &that);//重载非常量版本;移动赋值语义
A & operator=(const A & that);//重载常量版本;深拷贝赋值语义
virtual ~A() { if (_p) { delete[]_p, _p = nullptr; } }
public:
int & operator[](int i) throw(std::out_of_range);
const int & operator[](int i) const throw(std::out_of_range);
private:
int _n;
int *_p;
}; A::A(A & that)
{
_n = that._n, _p = that._p, that._n = , that._p = nullptr;
} A::A(const A & that)
{
this->_n = that._n;
_p = new int[_n];
for (int i = ; i < _n; i++)
{
_p[i] = that._p[i];
}
} A & A::operator=(A & that)
{
_n = that._n, _p = that._p, that._n = , that._p = nullptr;
return *this;
} A & A::operator=(const A & that)
{
this->_n = that._n;
if (_p)
{
delete[]_p;
}
_p = new int[_n];
for (int i = ; i < _n; i++)
{
_p[i] = that._p[i];
}
return *this;
}
//main.cpp
int main()
{
A a1;//缺省构造
const A a2;//缺省构造
A a3(a1);//调用A::A(A &),移动构造
A a4(a2);//调用A::A(const A &),深拷贝构造
//对于非常量,必须转型为常量才能进行深拷贝
A a5(const_cast<const A &>(a1));//调用A::A(const A &)
A a6, a7, a8;//缺省构造
a6 = a1;//调用A::operator=(A &),移动赋值
a7 = a2;//调用A::operator=(const A &),深拷贝赋值
a8 = const_cast<const A &>(a1);//调用A::operator=(const A &)
return ;
}

右值引用的意义

右值引用可以使用文字作为函数实际参数

//不接受文字作为实际参数,因无法获取文字的左值
int f(int &x) { return ++x; }
//接受文字作为实际参数,传递右值引用
//具名右值引用作为左值,匿名右值引用作为右值
//在函数内部理论如此,但实际上...
int f(int && x) { return ++x; }
int main()
{
//错误代码,++操作符的操作数必须为左值
//std::cout << ++10 << std::endl;
//可能有问题,传递右值引用,但部分编译器可能将其作为左值
std::cout << f() << std::endl;//11?
return ;
}

右值引用的意义

避免编写过多的构造与赋值函数

  • 不管是左值引用还是右值引用,若同时提供拷贝语义与移动语义,需要2对(4个)构造和赋值函数
  • 若通过单独提供成员值的方式构造对象,单成员至少需要4个构造函数和赋值函数,双成员至少需要8个构造和赋值函数
  • 使用右值引用,通过函数模板可以缩减代码编写量

实现完美转发

C++学习笔记13:运算符重载(赋值操作符2)的更多相关文章

  1. C++学习笔记之运算符重载

    一.运算符重载基本知识 在前面的一篇博文 C++学习笔记之模板(1)——从函数重载到函数模板 中,介绍了函数重载的概念,定义及用法,函数重载(也被称之为函数多态)就是使用户能够定义多个名称相同但特征标 ...

  2. C++基础 学习笔记五:重载之运算符重载

    C++基础 学习笔记五:重载之运算符重载 什么是运算符重载 用同一个运算符完成不同的功能即同一个运算符可以有不同的功能的方法叫做运算符重载.运算符重载是静态多态性的体现. 运算符重载的规则 重载公式 ...

  3. Effective C++(10) 重载赋值操作符时,返回该对象的引用(retrun *this)

    问题聚焦: 这个准则比较简短,但是往往就是这种细节的地方,可以提高你的代码质量. 细节决定成败,让我们一起学习这条重载赋值操作符时需要遵守的准则吧. 还是以一个例子开始: Demo // 连锁赋值 x ...

  4. 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理

    · 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...

  5. C++ 之 重载赋值操作符

    Widget 类中,定义了一个 Bitmap 类型的私有数据成员 -- pb 指针 class Bitmap { ... }; class Widget { private: Bitmap *pb; ...

  6. C++中复制构造函数与重载赋值操作符总结

    前言 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 1.复制构造函数和重载赋值操作符的定义: 2.复制构造函数和重载赋值操作符的调用时机: 3.复制构造函数和重载赋值操作符 ...

  7. Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法

    Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法 这篇笔记将介绍如何使用Ext.Net GridPanel 中使用Sorter. 默认情况下,Ext.Net GridP ...

  8. C++中复制构造函数与重载赋值操作符

    我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数.例如以下类:   class CTe ...

  9. SQL反模式学习笔记13 使用索引

    目标:优化性能 改善性能最好的技术就是在数据库中合理地使用索引.  索引也是数据结构,它能使数据库将指定列中的某个值快速定位在相应的行. 反模式:无规划的使用索引 1.不使用索引或索引不足 2.使用了 ...

  10. golang学习笔记13 Golang 类型转换整理 go语言string、int、int64、float64、complex 互相转换

    golang学习笔记13 Golang 类型转换整理 go语言string.int.int64.float64.complex 互相转换 #string到intint,err:=strconv.Ato ...

随机推荐

  1. 从体系架构上分析PRINCE2和pmp的区别

    一, 目标不同 PMP希望造就一个高效的项目经理,PRINCE2希望做成一个高效的项目. 二, 定位不同 PMP 是框架,PMBOK是指南,是统一化语音,是统一指导项目经理what to do. 而P ...

  2. [Maven]Eclipse插件之Maven配置及问题解析.

    前言:今天在自己环境装了Maven环境, 并且安装了Eclipse插件, 在查找插件过程中确实遇到一些问题, 好不容易找到一个  却又有问题.装好了插件之后, 用Eclipse创建Maven项目却出现 ...

  3. Atitit  发帖机实现(1)-----UsrQBm2008 页面上下文规范

    Atitit  发帖机实现(1)-----UsrQBm2008 页面上下文规范 1.1. 网站绝对路径,页面绝对路径1 1.2. Java的javax.servlet.ServletContext1 ...

  4. 修复发生“由于数据移动,未能继续以 NOLOCK 方式扫描”错误的数据库

    最近在系统运行中发现了一个错误,错误信息如下: 错误信息:查询A201412C20568单证信息错误 ---> System.Data.OleDb.OleDbException: 由于数据移动, ...

  5. 转 - ubuntu apache2下目录结构

    ubuntu apache2下目录结构 原文:http://blog.csdn.net/jibcy/article/details/8060651 在Windows下,Apache的配置文件通常只有一 ...

  6. 在 xampp 环境下安装 laravel

    网上搜了很多教程,很多都是采用命令行安装的.但是对于web前端来说,根本就不知道在干吗! 于是,打算写一篇文章,来教后面的人如何在 XAMPP 环境下安装 laravel. 初步认识 XAMPP(Ap ...

  7. 分享10条Visual Studio 2012的开发使用技巧

    使用Visual Studio 2012有一段时间了,并不是追赶潮流,而是被逼迫无可奈何.客户要求的ASP.NET MVC 4的项目,要用.NET 4.5来运行.经过一段时间的摸索,得到一点经验和体会 ...

  8. 浏览器兼容处理(HTML条件注释、CSSHack和JS识别)

    前面的话 本文中所有IEx+代表包含x及x以上:IEx-代表包含x及x以下,仅个人习惯.例:IE7+代表IE7.IE8…… 本文中所有例子全部经过测试,欢迎交流. HTML识别 条件注释法(IE10+ ...

  9. javaweb -- 获取请求IP(附实现源码)

    实现步骤如下: (1)新建一个java web工程 (2)新建servers包,新建类GetRequestIp,用来处理获取请求IP,GetRequestIp类完整代码如下: package serv ...

  10. isDebugEnabled有什么用?

    这几天在读Spring MVC源码时,发现了如下代码: if (logger.isDebugEnabled()) { logger.debug("Using ThemeResolver [& ...