一起学习交流~

c/c++细节知识点 10左值右值引用移动语义

c++面试准备 laomuji 4周前 (08-30) 60次浏览 已收录 0个评论

理论知识

/*
1.引用是什么?
在《C++ Primer Plus》一书中说 "引用是已定义变量的别名(另一个名称)" 但我认为并不准确
如果是别名 不应该占用内存,而引用是占用内存的,下面是我的推断过程
首先看一下指针和引用的反汇编代码
指针和引用的汇编代码完全相同,都是指向变量的地址
可以得出结论,现代编译器中,引用是占用内存的,大小与指针相同。

2.引用和指针是同一个东西吗?
不是,虽然在汇编语言层面,二者都是指向对象的地址
但是引用和指针的含义完全不同
指针的值表示变量的地址,指针的地址就是指针的地址
而引用的值就是变量的值,引用的地址就是变量的地址

3.为什么要使用引用?
减少临时变量的复制

4.引用和指针的兼容性
基类指针或者引用都可以指向派生类对象

5.右值引用
将右值关联到右值引用导致右值被存储到特定的位置,并且可以获取到该位置的地址
通过数据与特定地址关联,使得可以通过右值引用来访问该数据

6.左值引用与右值引用和移动语义的关系
左值引用执行复制函数
右值引用执行移动函数
在引入右值引用之前(C98),两个左值相加的结果将会调用复制构造函数
但左值引用不能指向右值,所以必须在前面加上const引用
const引用形参将会指向一个临时变量,这个临时此时使用构造函数或赋值函数,都会导致有额外的复制操作
有些比较机智的编译器可能会自动消除额外的复制,通过右值引用可以由程序员指定何时使用移动语义

7.强制使用右值引用
将对象强制转换为右值引用类型
在C++11以前 使用static_cast强制转换
在C++11使用utility头文件中的std::move
*/

指针和引用在汇编中

反汇编指针和引用 发现并没有区别

#include<iostream>
#include<string>
using namespace std;
int main10_1() {
    int a;
    cin >> a;
    int* pa = &a;
    int& ra = a;
    cout << *pa << " " << ra << endl;
    return 0;
}

右值引用绑定右值

int main10_2() {
    //右值引用与右值绑定,通过右值引用来访问数据
    int&& ri = 123;
    cout << ri << " " << &ri << endl;
    ri = 456;
    cout << ri << " " << &ri << endl;
    int x = 666;
    int y = 321;
    //int&& rx = x;//x为左值,所以不能被右值引用绑定
    int&& ry = static_cast<int&&>(y);//但一个左值可以被强行转换为右值
    //本身没有地址的右值123 被右值引用绑定
    cout << "强制转换左值产生临时变量" << &y << " " << &ry << endl;
    int&& rxy = x + y;//x+y的结果是右值,所以可以使用
    return 0;
}

右值引用在类中的用法

使用右值引用可以消除多余的复制操作
使何时使用移动语义由程序员决定而不是由编译器

class Class_10_CHARS{
private:
    char* chs = nullptr;
    int m_len = 0;
public:
    ~Class_10_CHARS() {
        if (chs != nullptr) {
            delete[] chs;
            cout << "析构函数被调用,进行了回收" << endl;
        }
        else {
            cout << "析构函数被调用,不需要回收" << endl;
        }
    }

    Class_10_CHARS() {
        cout << "无参构造函数被调用" << endl;
        chs = nullptr;
        m_len = 0;
    }

    Class_10_CHARS(const char * str) {
        cout << "const char* 构造函数被调用" << endl;
        m_len = strlen(str);
        chs = new char[m_len + 1];
        memcpy(chs, str, m_len);
        chs[m_len] = '\0';
    }

    //复制构造函数
    Class_10_CHARS(const Class_10_CHARS& target) {
        cout << "复制构造函数被调用" << endl;
        m_len = strlen(target.chs);
        chs = new char[m_len + 1];
        memcpy(chs, target.chs, m_len);
        chs[m_len] = '\0';
    }

    //移动构造函数,一般不加const,因为一般会移交所有权
    Class_10_CHARS(Class_10_CHARS&& target) noexcept {
        cout << "移动构造函数被调用" << endl;
        m_len = target.m_len;
        chs = target.chs;
        target.m_len = 0;
        target.chs = nullptr;
    }

    //重载+
    Class_10_CHARS operator+(const Class_10_CHARS&target) const{
        cout << "+被调用" << endl;
        Class_10_CHARS ans;
        int len = this->m_len + target.m_len;
        ans.chs = new char[len + 1];
        ans.chs[len] = '\0';
        ans.m_len = len;
        memcpy(ans.chs, this->chs, this->m_len);
        memcpy(ans.chs + this->m_len, target.chs, target.m_len);
        return ans;
    }

    void showData() {
        cout << chs << endl;
    }
};
int main10_3() {
    Class_10_CHARS a("123");
    Class_10_CHARS b = a;//复制构造函数,因为a是左值
    Class_10_CHARS c = a + b;//a+b是右值,所以调用了移动构造函数
    c.showData();
    return 0;
}

int main10_4() {
    Class_10_CHARS a("asd123");
    //以下几种方法都可以强行把左值当作右值,且都只会调用移动函数而不是复制
    Class_10_CHARS b = static_cast<Class_10_CHARS&&>(a);
    Class_10_CHARS c = (Class_10_CHARS&&)b;
    Class_10_CHARS d = std::move(c);
    return 0;
}

喜欢 (0)
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论