C++11 (六) - 右值引用


一、左值和右值

1. 左值

左值就是非临时的,有名字的,可以取地址的变量,可以在多条语句中使用

2. 右值

右值是指临时的对象,它们只在当前的语句中有效,包括函数返回的临时变量,常量值如 1,'a',true,lambda 表达式

二、右值引用

右值引用是 C++ 新标准中引入的新特性 , 主要用于避免使用临时对象构造和赋值新对象时的不必要拷贝,避免了临时对象分配,拷贝,释放的系统开销,提高了效率
移动构造函数接受一个”右值引用”参数,可以理解成临时对象的引用
在移动构造函数中,将新对象里的指针成员指向该临时对象分配的空间,而把临时对象的指针成员设为 nullptr,这样临时对象析构的时候不会释放掉分配的空间
这种直接使用临时对象已申请的空间,并在其析构函数中取消对空间的释放,这样既能节省空间,又能节省资源申请和释放的时间,相当于复用临时对象申请的空间,延长该空间的生命周期,这正是定义移动语义的目的

三、示例代码

  • 不使用右值引用
#include <iostream>
#include <vector>
using namespace std;

class iString {
public:
    iString() {
        m_data = nullptr;
        m_len = 0;
        cout << "构造函数: nullptr" << endl;
    }

    iString(const char* s) {
        m_len = strlen(s);
        init_data(s);
        cout << "构造函数: " << m_data << endl;
    }

    iString(const iString& str) {
        m_len = str.m_len;
        init_data(str.m_data);
        cout << "拷贝构造函数: " << m_data << endl;
    }

    iString& operator=(const iString& str) {
        if (this != &str) {
            this->m_len = str.m_len;
            init_data(str.m_data);
        }
        cout << "等号运算符重载函数: " << m_data << endl;
        return *this;
    }

    ~iString() {
        if (m_data) {
            cout << "析构函数: " << m_data << endl;
            free(m_data);
        }
    }

    void printString() {
        cout << "打印字符串: " << m_data << endl;
    }

private:
    void init_data(const char* s) {
        m_data = new char[m_len + 1];
        memcpy(m_data, s, m_len);
        m_data[m_len] = '\0';
    }

    char* m_data;
    size_t m_len;
};

void swapiString(iString& a, iString& b) {
    iString temp(std::move(a));  // 调用拷贝构造函数或者移动构造函数
    a = std::move(b);            // 调用等号运算符重载函数或者移动等号运算符重载函数
    b = std::move(temp);
}

int main() {
    vector<iString> vec;
    iString a;
    a = iString("hello");
    vec.push_back(iString("world"));

    swapiString(a, vec.back());
    a.printString();
    vec.back().printString();

    return 0;
}

/*
output: 不使用右值引用
构造函数: nullptr
构造函数: hello
等号运算符重载函数: hello
析构函数: hello
构造函数: world
拷贝构造函数: world
析构函数: world
拷贝构造函数: hello
等号运算符重载函数: world
等号运算符重载函数: hello
析构函数: hello
打印字符串: world
打印字符串: hello
析构函数: world
析构函数: hello
*/

总共执行了2次拷贝构造,iString("Hello")iString("World") 都是临时对象,临时对象被使用完之后被立即析构,在析构函数中释放掉申请的内存资源

  • 使用右值引用
#include <iostream>
#include <vector>
using namespace std;

class iString {
public:
    iString() {
        m_data = nullptr;
        m_len = 0;
        cout << "构造函数: nullptr" << endl;
    }

    iString(const char* s) {
        m_len = strlen(s);
        init_data(s);
        cout << "构造函数: " << m_data << endl;
    }

    iString(iString&& str) { // 用临时对象进行对象构造,才会触发移动构造
        m_len = str.m_len;
        m_data = str.m_data;
        str.m_len = 0;
        str.m_data = nullptr; // 阻止 str 对象析构函数中释放内存
        cout << "移动构造函数: " << m_data << endl;
    }

    iString& operator=(iString&& str) { // 用临时对象进行赋值操作,才会触发移动赋值运算
        if (this != &str) {
            this->m_len = str.m_len;
            this->m_data = str.m_data;
            str.m_len = 0;
            str.m_data = nullptr; // 阻止 str 对象析构函数中释放内存
        }
        cout << "移动等号运算符重载函数: " << m_data << endl;
        return *this;
    }

    ~iString() {
        if (m_data) {
            cout << "析构函数: " << m_data << endl;
            free(m_data);
        }
    }

    void printString() {
        cout << "打印字符串: " << m_data << endl;
    }

private:
    void init_data(const char* s) {
        m_data = new char[m_len + 1];
        memcpy(m_data, s, m_len);
        m_data[m_len] = '\0';
    }

    char* m_data;
    size_t m_len;
};

void swapiString(iString& a, iString& b) {
    iString temp(std::move(a)); // 调用拷贝构造函数或者移动构造函数
    a = std::move(b);            // 调用等号运算符重载函数或者移动等号运算符重载函数
    b = std::move(temp);
}

int main() {
    vector<iString> vec;
    iString a;
    a = iString("hello");
    vec.push_back(iString("world"));

    swapiString(a, vec.back());
    a.printString();
    vec.back().printString();

    return 0;
}

/*
output: 使用右值引用
构造函数: nullptr
构造函数: hello
移动等号运算符重载函数: hello
构造函数: world
移动构造函数: world
移动构造函数: hello
移动等号运算符重载函数: world
移动等号运算符重载函数: hello
打印字符串: world
打印字符串: hello
析构函数: world
析构函数: hello
*/

使用右值引用能够直接使用临时对象已经申请的资源,并在其析构函数中取消对资源的释放,这样既能节省资源,又能节省资源申请和释放的时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值