• C++11中的右值引用和move函数


    新版的C++标准库出现了很多C++11的新特性,刚开始接触确实很费劲,特别是右值引用和move函数这种基于效率的考虑损失了语言的简单直接的特性,废话不多说,先看右值引用。

    C++中根据const和non_const,lvalue和rvalue可分为四类对象


            non_const          const

    lvalue  非常量左值         常量左值

    rvalue  非常量右值         常量左值


    如果使用C++03标准,函数的参数选择const reference(常量引用),可以接受所有的传值类型:常量/非常量的左值/右值,即:

    void f(const T& t);

    可以接受所有T类型的对象传入。

    而void f(T& t);

    只能接受非常量的左值传入。

    我们没有办法在函数参数类型区分只接受左值,或右值,要办到这件事,需要引入右值引用的概念。

    void f(T&& t);

    T&&不是引用的引用,是右值引用。C++11(准确的说是C++09)引入此项特性的目的是了健全左右值的引用。

    为什么要区分?

    考虑下面的函数,它已经被用烂了:

    vector<int> f(vector<int>& p)
    {
        vector<int> a;
        for(int i = 0 ;i < 100; i++)
            a.push_back(p+i);
        return a;
    }
    vector<int> test = f(v);//v is a vector

    这样的函数行为导致vector<int>a的初始化是一种浪费的行为,它创建了一段在堆上的空间存放数据,再把这些数据拷贝给vector<int> test。

    我们完全可以把vector<int> a在堆上申请的空间以及被设置好的数据直接给test,告诉test你拿去用好了,反正我马上就死了。

    好了,如果我们的vector中有一个构造函数,能够拿test的空间和数据,那么上面的过程也就完成了:

    vector<int>(vector<int>&& v)//vector<int>的构造函数的重载版本
    {//这是简化版的vector,只是为了让你看懂move的意义所在
        ptr = v.ptr;//假设ptr是指向存储地址的指针
        size = v.size;//size是vector中存储空间的大小
       ....
      v.ptr = 0;//neccessary! }

    注意到构造函数的参数是vector<int>&&,它只接受右值引用的传入。

    为什么要只接受右值的引用?因为,我们只想要马上就销毁的对象才把数据move过来,而其他还要继续使用的对象,数据是拷贝过来的(通过其他的拷贝构造函数的重载版本实现)

    同时,我们的f函数需要修改如下:

    vector<int> f(vector<int>& p)//注意返回类型不是vector<int>&&
    {
        vector<int> a;
        for(int i = 0 ;i < 100; i++)
            a.push_back(p+i);
        return std::move(a);
    }

    move的作用是把对象修改为一个右值的引用。

    有三个地方需要注意:

    1. 参数的符号必须是右值引用符号,即“&&”。

    2. 参数不可以是常量,因为我们需要修改右值。

    3. 参数的资源链接和标记必须修改。否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。

    特别地,针对C++11中的右值引用有三个值得注意的特别规则,它们为右值引用特别定义。

    有这么一个模板函数:

    template<class T> void f(T&& a);

    1.在模板推断时,如果传给f()实参a的变量是一个左值,假设这个变量是U类型的,这个时候T被推断为U&,而不是U;

    2.引用的折叠。如果T被推断为U&,函数f变成:f(U& &&),发生引用折叠,U& && 变为U&。(注意,我们不能直接创建一个引用的引用。)

    3.不能隐式的将一个左值转换为右值引用,但可以用static_cast显示将一个左值转换为一个右值引用。

    好了,如果没有右值引用,我们就没法完成上述功能。

    推荐一篇博文,讲得比较详细http://blog.csdn.net/pongba/article/details/1684519

  • 相关阅读:
    函数模板、函数模板特化、重载函数模板、非模板函数重载
    输出流格式化(以操纵子方式格式化,以ios类成员函数方式格式化)
    文件的读写、二进制文件的读写、文件随机读写
    文件流(fstream, ifstream, ofstream)的打开关闭、流状态
    流类库继承体系(IO流,文件流,串流)和 字符串流的基本操作
    对象语义与值语义、资源管理(RAII、资源所有权)、模拟实现auto_ptr<class>、实现Ptr_vector
    operator new 和 operator delete 实现一个简单内存泄漏跟踪器
    异常与继承、异常与指针、异常规格说明
    程序错误、异常(语法、抛出、捕获、传播)、栈展开
    C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
  • 原文地址:https://www.cnblogs.com/wangpei0522/p/4472258.html
Copyright © 2020-2023  润新知