1、引用

C++中的引用主要用作函数的形参,接近于const指针,必须在创建时初始化。

以Person类为例,如下:

Person p;                          //调用P的构造函数,创建对象P

Person &p2 = p;                //引用变量P2指向P

Person p3 = p2;                //P2是引用,创建一个p3的对象,会调用Person的拷贝构造函数,p3和p不是一个对象。

Person &P4 = get(p);  //形参是引用,所以参数传入时不会再生成一个临时对象。

      //返回一个引用,并将P4指向该引用。

Person P5 = get(p);          //形参是引用,所以参数传入时不会再生成一个临时对象。

//返回一个引用,由于要创建一个P5的对象,所以会调用拷贝构造函数生成一个对象。

Person& get(Person  &p)

{

return p;

}

实际上,现在的C++标准对于形参为const引用的C++函数,如果实参不匹配,那么编译器会生成临时变量,其行为也类似按值传递,为确保原始数据不被修改,将使用临时变量来存储值。所以尽可能地使用const。

上述提到的实参与引用参数不匹配的情况主要有两种:

1、实参类型正确,但是不是左值。

2、实参类型不正确,但是可以转换为正确的类型。

那什么是左值呢?左值参数即是可以被引用的数据对象,程序可以获取其地址,例如,变量、数组、对象、解引用的指针等都是左值。非左值包括字面常量(用引号括起来的字符串不算)和多项式,以及函数返回的临时对象(引用除外)。const变量也是左值,是一种特殊的左值,属于不可修改的左值。

以上的引用我们又称之为左值引用,C++11中新增了另一种引用,即右值引用。这种引用可以指向右值,使用&&声明。

double &&r3 = 5 + 9;//r3关联到13

将右值关联到右值引用将导致该右值被存储到特定的位置,且可以获取该位置地址。即我们虽然不能将取地址符用于14,但是我们可以用在r3上。这样我们就可以使用右值引用来访问该数据。

引入右值引用的主要目的之一是实现移动语义。

2、构造函数、拷贝构造函数、赋值运算符

在讲移动语义之前,先说一下构造函数、拷贝构造函数、赋值构造函数。C++为我们提供了4个特殊的成员函数,除了上面3个以外还包括析构函数,这里就不多说了。

Person();

Person(const Person & p);

Person& operator=(const Person &p);

函数原型如上所示,下面讲一下拷贝构造函数和赋值运算符的区别,以及何时会调用拷贝构造函数,何时会调用赋值运算符。

两者最根本区别在于:拷贝构造函数是用来创建对象,赋值运算符是用来将一个对象的值复制给另外一个对象。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。

以下同样也Person为例

Person p = Person();    //构造函数

Person p1 = p;      //p1对象不存在,p对象存在,调用拷贝构造函数

p1 = p;             //p1和p对象都存在,调用赋值运算符

Person p2(p);           //p2对象不存在,p对象存在,调用拷贝构造函数

Test::get();            //函数内部p对象创建,调用构造函数

//函数返回时临时对象不存在,p对象存在,调用拷贝构造函数

//释放内部p对象;接着释放临时对象

Person p3 = Test::get();//函数内部p对象创建,调用构造函数

//函数返回时临时对象不存在,p对象存在,调用拷贝构造函数

//释放内部p对象;

Test::get(p3);      //什么都不调用

Person P4 = Test::get(p3);//调用一次拷贝构造函数,生成对象P4

class Test

{

public:

static Person& get(Person  &p)

{

return p;

}

static Person get()

{

Person p;

return p;

}

};

3、移动语义、移动构造函数、移动赋值运算符

首先我们分析下为什么需要移动语义。

先看一下C++11之前的复制过程

Person p(get());

static Person get()

{

Person p2;

return p2;

}

首先会创建函数内对象p2,然后调用拷贝构造函数创建一个临时对象,接着再调用拷贝构造函数创建对象p,然后再将临时对象删除。这时就做了冗余的工作,如果直接将p与临时对象关联起来,那岂不就少做了很多工作吗?这类似于计算机中移动文件的情形:实际文件还留在原来的地方,而只是修改了记录,这种方法我们称为移动语义。移动语义实质上没有移动数据,而只是修改了记录。

怎么样才能使用移动语义呢?

必须让编译器知道什么时候需要使用什么时候不需要使用。

首先我们定义一个移动构造函数,这时就可以使用右值引用了,它使用右值引用作为参数,该引用关联到右值实参,负责调整记录,将所有权转移给新对象的过程中,移动构造函数可能会修改其实参,这意味着右值引用参数不应该是const。移动构造函数如下所示

Person(Person &&p)

使用的时候,需要传入右值。如下所示

Person P(P1+P2);

移动语义只在消除额外的工作,机智的编译器可能自动消除额外的赋值工作,但通过使用右值引用,程序员可以指出何时使用移动语义。

除了构造函数外,赋值运算符也可以使用移动语义。原型如下

Person& operator=( Person && p);

移动构造函数和移动赋值运算符都是用右值,如果想要使用左值,该如何办呢?可以使用std::move函数,该函数将左值转换成右值。对于大多数程序员来说,右值引用的好处并非让他们能够编写使用右值引用的代码,而是能够利用右值引用实现移动语义的库代码。例如,STL类现在都有拷贝构造函数,移动构造函数,复制赋值运算符,移动赋值运算符。

通常情况下编译器将提供6个特殊的成员函数,但是,如果您提供了析构函数、拷贝构造函数和复制赋值运算符,那么编译器将不提供移动构造函数和移动赋值运算符,相反,如果提供了移动构造函数和移动赋值运算符,那么编译器将不会提供拷贝构造函数和复制运算符。

C++ 引用、构造函数、移动语义的更多相关文章

  1. 【转】C++11 标准新特性: 右值引用与转移语义

    VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...

  2. C++11 标准新特性: 右值引用与转移语义

    文章出处:https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/ 新特性的目的 右值引用 (Rvalue Referene) ...

  3. C++11 右值引用和转移语义

    新特性的目的 右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和 ...

  4. C++11中右值引用和移动语义

    目录 左值.右值.左值引用.右值引用 右值引用和统一引用 使用右值引用,避免深拷贝,优化程序性能 std::move()移动语义 std::forward()完美转发 容器中的emplace_back ...

  5. C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

    1. 右值引用 个人认为右值引用的目的主要是为了是减少内存拷贝,优化性能. 比如下面的代码: String Fun() { String str = "hello world"; ...

  6. C++11 右值引用 与 转移语义

    新特性的目的 右值引用(R-value Reference)是C++新标准(C++11, 11代表2011年)中引入的新特性,它实现了转移语义(Move Semantics)和精确传递(Perfect ...

  7. [c++11]右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  8. [转][c++11]我理解的右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  9. Effective Modern C++:05右值引用、移动语义和完美转发

    移动语义使得编译器得以使用成本较低的移动操作,来代替成本较高的复制操作:完美转发使得人们可以撰写接收任意实参的函数模板,并将其转发到目标函数,目标函数会接收到与转发函数所接收到的完全相同的实参.右值引 ...

  10. 关于C++11右值引用和移动语义的探究

    关于C++11右值引用和移动语义的探究

随机推荐

  1. 在Ubuntu中使用JDB调试代码

    在Ubuntu中使用JDB调试代码 了解JDB JDB是JDK安装的一部分,是基于文本和命令行的调试工具,JDB是可以免费获取且平台独立的,缺点是只有命令行格式. JDB基础命令 在方法中设置断点st ...

  2. 使用Badboy录制Web脚本 JMeter运行jmx脚本

    1.下载JDK 1.1 官网地址:https://www.oracle.com/technetwork/java/javase/downloads/index.html 在官网下载最新版本的JDK 1 ...

  3. html转化为图片下载

    业务需求:按照客户要求把排课表转化为图片下载到本地.一个月到排课有很多.所以图片会很大 <!DOCTYPE html> <html lang="en"> & ...

  4. huawei USG防火墙子接口技术的应用案例

    网络拓扑: 在中小企业的办公网络的设计中,通常为了安全考虑,需要将不同部门之间的互访的流量经过防火墙中转,同时结合vpn stance将业务进行隔离,在本例中,使用两台USG防火墙做HA作为整个办公网 ...

  5. Harry Potter and J.K.Rowling(半平面交+圆和矩形交)

    Harry Potter and J.K.Rowling http://acm.hdu.edu.cn/showproblem.php?pid=3982 Time Limit: 2000/1000 MS ...

  6. java-27 Properties类的使用

    1.Properties Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串. 2.方法 setProperty( ...

  7. 《Java从入门到精通》学习总结1

    1. Java既是编译型语音,也是解释型语言:先将源代码编译成Java字节码,然后Java虚拟机对Java字节码进行解释运行 2. 使用命令行编译Java源代码时,如果代码中有中文,在编译时需要指定编 ...

  8. 为什么text的值改变后onchange没有反应?

    onchange发生在元素失去焦点后,而不是想象中的元素的值发生改变的时候.其实它的作用就跟onblur(失去焦点事件)差不多,只不过onchange是失去焦点且值发生了改变.要想实现目的,可以改用o ...

  9. vue slot 复用

    话不投机半句多,直接上代码 有3步 第一步:创建渲染slot的组件 重要 第二步:为slot添加父组件数据(props) 重要 第三步:使用 第一步:创建渲染slot的组件 首选创建一个单文件组价,由 ...

  10. Oracle使用触发器和mysql中使用触发器的比较

    一.触发器 1.触发器在数据库里以独立的对象存储, 2.触发器不需要调用,它由一个事件来触发运行 3.触发器不能接收参数 --触发器的应用 举个例子:校内网.开心网.facebook,当你发一个日志, ...