c++积累8-右值引用、移动语义

这篇具有很好参考价值的文章主要介绍了c++积累8-右值引用、移动语义。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、右值引用

1.1 背景

c++98中的引用很常见,就是给变量取个别名,具体可以参考c++积累7
在c++11中,增加了右值引用的概念,所以c++98中的引用都称为左值引用

1.2 定义

右值引用就是给右值取个名字,右值有了名字之后就成了普通变量,可以像使用左值一样使用。

语法:数据类型&& 变量名=右值

示例:

#include <iostream>

class AA {
public:
    int m_a = 9;
};

AA getTemp() {
    return AA();
}

int main() {
    using namespace std;

    int &&a = 3; // 3是右值,给它起个名字叫a
    int b = 8; // b 是左值, 8是右值
    int &&c = b + 5; // b+5是右值,给它取个名字叫c

    AA &&aa = getTemp();// getTemp()返回值是右值(临时变量),给它起个名字叫aa

    cout << "a= " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    cout << "aa.m_a= " << aa.m_a << endl;

    return 0;
}

c++积累8-右值引用、移动语义

1.3 常量左值引用

常量左值引用是一个万能的引用类型,它可以绑定非常量左值、常量做值、右值,在绑定右值的时候,常量左值引用可以像右值引用一样将右值的生命期延长,缺点是只能读不能改

    int a = 1;
    const int &ra = a; // a是非常量左值
    const int b = 2;
    const int &rb = b; // b是常量左值
    const int &rc = 1; // 1是右值

2、移动语义

2.1 背景

如果一个对象中有堆区资源,需要编写拷贝构造函数和赋值函数,实现深拷贝。
深拷贝把对象中堆区资源复制了一份,如果资源(被拷贝的资源)是临时对象,拷贝完就没有什么意义了,这样会造成没有意义的资源申请和释放操作。
如果能够直接使用对象拥有的资源,可以节省资源申请和释放的时间。c++11增加的移动语义就能够做到这一点。

2.2 定义

移动语义增加两个构造函数:移动构造函数 、 移动赋值函数
移动构造函数语法:
类名(类名&& 源对象){…}
移动赋值函数语法:
类名& operator=(类名&& 源对象){…}

demo:

#include <iostream>
#include <string.h>

using namespace std;

class AA {
public:
    int *m_data = nullptr; //数据成员,指向堆区资源的指针
    AA() = default; // 启用默认构造函数

    void alloc() {  // 给数据成员m_data分配内存
        m_data = new int;  // 分配内存
        memset(m_data, 0, sizeof(int)); //初始化已分配的内存
    }

    AA(const AA &a) {  //拷贝构造函数 - 拷贝语义
        cout << "调用了拷贝构造函数 。\n";  // 显示自己被调用的日志
        if (m_data == nullptr) alloc(); // 如果没有分配内存,就分配
        memcpy(m_data, a.m_data, sizeof(int)); //把数据从源对象中拷贝过来
    }

    AA(AA &&a) {  //拷贝构造函数 - 移动语义
        cout << "调用了移动语义拷贝构造函数 。\n";  // 显示自己被调用的日志
        if (m_data != nullptr) delete m_data; // 如果已经分配内存,先释放
        m_data = a.m_data; // 把资源从源对象中转移过来
        a.m_data = nullptr; // 把源对象中的指针置空
    }

    AA &operator=(const AA &a) { //赋值函数 - 拷贝语义
        cout << "调用了赋值函数。\n"; // 显示自己被调用的日志
        if (this == &a) return *this; // 避免自我赋值
        if (m_data == nullptr) alloc(); // 如果没有分配内存,就分配
        memcpy(m_data, a.m_data, sizeof(int)); // 把数据从源对象中拷贝过来
        return *this;
    }

    AA &operator=(AA &&a) { //赋值函数 - 移动语义
        cout << "调用了移动语义赋值函数。\n"; // 显示自己被调用的日志
        if (this == &a) return *this; // 避免自我赋值
        if (m_data != nullptr) delete m_data; // 如果已经分配内存,先释放
        m_data = a.m_data; // 把资源从源对象中转移过来
        a.m_data = nullptr; // 把源对象中的指针置空
        return *this;
    }

    ~AA() { // 析构函数
        cout << "调用析构函数" << endl;
        if (m_data != nullptr) {
            delete m_data;
            m_data == nullptr;
        }
    }

};

int main() {
    AA a1; // 创建对象a1
    a1.alloc(); // 分配堆区资源
    *a1.m_data = 3; // 给堆区内存赋值
    cout << "*a1.m_data = " << *a1.m_data << ",addr = " << a1.m_data << endl;

    AA a2 = a1; // 调用拷贝构造函数 - 这个地方a1是左值就调用拷贝语义构造函数,如果是右值,则调用移动语义构造函数
    cout << "*a2.m_data = " << *a2.m_data << ",addr = " << a2.m_data << endl;

    AA a3;
    a3 = a1; // 调用赋值函数
    cout << "*a3.m_data = " << *a3.m_data << ",addr = " << a3.m_data << endl;

    auto f = [] { // 返回AA类对象的lambda函数
        AA aa;
        aa.alloc();
        *aa.m_data = 10;
        return aa;
    };
    AA a4 = f(); // lambda函数返回临时对象,是右值,将调用移动构造函数
    cout << "*a4.m_data = " << *a4.m_data << ",addr = " << a4.m_data << endl;

    AA a6;
    a6 = f(); // lambda函数返回临时对象,是右值,将调用移动赋值函数
    cout << "*a6.m_data = " << *a6.m_data << ",addr = " << a6.m_data << endl;

    return 0;
}

c++积累8-右值引用、移动语义

2.3、说明

1 std::move() 左值转换为右值
对于一个左值,会调用拷贝构造函数,但是有些左值是局部变量,声明周期也很短,我们也想使用移动,C++为了解决这种问题,提供了std::move()方法来将左值转义为右值,从而方便使用移动语义。
左值对象被转移资源后,不会立刻析构,只有在离开自己的作用域的时候才会析构,如果继续使用左值的资源,可能会发生意想不到的错误。
2 没有提供移动构造、赋值函数,使用拷贝构造、赋值函数
如果没有提供移动构造/赋值函数,只提供了拷贝构造/赋值函数,编译器找不到移动构造/赋值函数就会去寻找拷贝构造/赋值函数
3 c++11中的所有容器都实现了移动语义,避免对含有资源的对象发生无畏的拷贝
4 移动语义对于拥有资源(如内存、文件句柄)的对象有效,如果是基本类型,使用移动语义没有意义

3、完美转发 std::forward()

如果模版中(包含类模版和函数模版)函数的参数写成 T&& 参数名, 那么函数既可以接收左值引用,右可以接受右值引用。

模版函数:std::forward(参数)用于转发参数。如果参数是一个右值,转发之后仍是右值引用,如果参数是一个左值,转发之后仍是左值引用。

#include <iostream>

using namespace std;
void func1(int &i ){
    cout<< "参数是左值"<< i << endl;
}

void func1(int && i){
    cout << "参数是右值" << i << endl;
}

//template<typename T>
//void func2(T &i){
//    cout << "func2 1111" << endl;
//    func1(i);
//}
//
//template<typename T>
//void func2(T&& i){
//    cout << "func2 2222" << endl;
//    func1(move(i));
//}

template<typename T>
void func2(T&& i){
    func1(forward<T>(i));
}

//void func2(int& i){
//    func1(i);
//}
//
//void func2(int&&i ){
//    func1(move(i));
//}

int main(void) {
    int a = 3;

    func2(a);
    func2(9);
}

std::forward文章来源地址https://www.toymoban.com/news/detail-421890.html

到了这里,关于c++积累8-右值引用、移动语义的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++右值引用(左值表达式、右值表达式)(移动语义、完美转发(右值引用+std::forward))(有问题悬而未决)

    在 C++ 中,表达式可以分为左值表达式和右值表达式。左值表达式指的是可以出现在赋值语句左边的表达式,例如变量、数组元素、结构体成员等;右值表达式指的是不能出现在赋值语句左边的表达式,例如常量、临时对象、函数返回值等。 右值是指将要被销毁的临时对象或

    2024年02月04日
    浏览(33)
  • 【C++】 C++11(右值引用,移动语义,bind,包装器,lambda,线程库)

    C++11是C++语言的一次重大更新版本,于2011年发布,它包含了一些非常有用的新特性,为开发人员提供了更好的编程工具和更好的编程体验,使得编写高效、可维护、安全的代码更加容易。 一些C++11的新增特性包括: 强制类型枚举,使得枚举类型的通常行为更加可靠和容易控制

    2024年02月10日
    浏览(34)
  • C++ Primer阅读笔记--对象移动(右值引用、移动迭代器和引用限定符的使用)

    目录 1--右值引用 2--std::move 3--移动构造函数 4--移动赋值运算符 5--移动迭代器 6--引用限定符         右值引用必须绑定到右值的引用,通过 获得右值引用;         右值引用只能绑定到 临时对象 (即将被销毁的对象),即所引用的对象将要被销毁,对象没有其他用户;

    2024年02月10日
    浏览(27)
  • 【C++干货铺】C++11新特性——右值引用、移动构造、完美转发

    ========================================================================= 个人主页点击直达:小白不是程序媛 C++系列专栏:C++干货铺 代码仓库:Gitee ========================================================================= 目录 左值与左值引用 右值与右值引用 左值引用和右值引用的比较  左值引用总结:

    2024年01月25日
    浏览(30)
  • C++右值引用,右值引用与const引用的区别

    左值:可以取地址的、有名字的变量,有持久性; 右值:一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象,短暂性的。 C++11新增了另一种引用——右值引用。这种引用可指向右值,使用声明。 右值引用只能引用临时变量和常量值。 const引用:可以引用普

    2024年01月18日
    浏览(52)
  • 详解 C++ 左值、右值、左值引用以及右值引用

    左值是一个表示数据的表达式,比如: 变量名、解引用的指针变量 。一般地,我们可以 获取它的地址 和 对它赋值 ,但被 const 修饰后的左值,不能给它赋值,但是仍然可以取它的地址。 总体而言,可以取地址的对象就是左值。 右值也是一个表示数据的表达式,比如: 字面

    2023年04月08日
    浏览(37)
  • 【C++11】 initializer_list | 右值引用 | 移动构造 | 完美转发

    { } 初始化 C++11 扩大了括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义类型, 使用初始化列表,可添加等号(=),也可不添加 将1赋值给x1,x2处省略了赋值符号,将5赋值给x2 同样也可以将new开辟4个int的空间初始化为0 创建对象时,可以使用列

    2024年02月08日
    浏览(58)
  • [开发语言][c++]:左值、右值、左值引用、右值引用和std::move()

    写在前面: 如果你也被 左值、右值、左值引用、右值引用和std::move 搞得焦头烂额,相关概念和理解不够深入,或者认识模棱两可,那么这篇文章将非常的适合你,耐心阅读,相信一定会有所收获~~ 左值: 可以取地址、位于等号左边 – 表达式结束后依然存在的持久对象

    2024年02月02日
    浏览(51)
  • C++右值引用(&&)

    右值引用是 C++11 新增的特性之一,它是我们在日常开发工作中不断接触到的特性之一。本篇博客将对右值引用的定义、使用场景以及使用方法进行详细介绍。 右值引用是一种新的引用类型,“右值引用” 又被称为“具名右值引用”(Named Rvalue Reference),其定义形式为:Type v

    2024年02月07日
    浏览(28)
  • 【C++】右值引用

    引用是给对象取别名,本质是为了减少拷贝 。以前我们学习的引用都是左值引用,右值引用是C++11新增的语法,它们的共同点都是给对象取别名。既然如此,有了左值引用,为什么还要有右值引用?右值引用具体是怎样的?以及它有哪些应用场景?接下来,会详细分析~~ 左值

    2024年04月11日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包