C++中左值 右值 左值引用 右值引用 移动语义

1.左值和右值 lvaluervalue

一种解释为lvalue = left value, rvalue = right value;另一种解释 lvalue=located value,即可寻址的值,rvalue=read value,即只读的值。

int a = 10; 
int 10 = a; //错误

int b = a;  //等号右边不一定是右值

左值是存储在内存单元中的,有具体的地址可寻;而右值临时变量,字面常量,没有固定的存储单元,用完即消亡。所以可以用右值给左值幅值,但是不可以用左值给右值赋值。

2.左值引用和右值引用

2.1 左值引用 &

对左值的引用称为左值引用。不能用左值引用去引用右值,但是可以用常量左值引用去引用右值。

int  a = 10;

int &b = a; //正确 b是左值引用
int &b = 20; //错误,不能用左值引用去引用右值
const int &b=20; //正确,可以使用常量左值引用去引用右值

2.2右值引用 &&

希望函数只接受临时变量,实现针对零时变量的函数重载。c++中用两个 && 表示右值引用。

int &&a = 10;//右值引用可以指向临时变量
a = 20;     //正确,可以修改引用的临时值

此时如果有一个常量左值引用的函数重载,和右值引用的函数重载,编译器会优先调用右值引用的函数重载。

#include<iostream>
using namespace std;


void print(const int& a)
{
  cout<<"const int&"<<endl;
}

void print(int&& a)
{
  cout<<"int&&"<<endl;
}

int main()
{
    int a = 10;
    print(a); 
    print(10);
}

输出结果为:

const int&
int&&

3.移动语义

3.1 移动构造函数

c++中,很多情况下不需要或者不想把一个对象从一个地方复制到另一个地方。例如:如果把一个对象传递给一个函数,函数需要获得对象的所有权,不得不执行拷贝。需要在当前堆栈中构造一个一次性对象,然后幅值到正调用的函数。所以,如果只是移动对象而不去幅值,性能会更高。通过下面例子说明:

#include<iostream>
using namespace std;

class String
{
public:
    String() = default;

    String(const char* string)
    {
        printf("Created!\n");
        m_size = strlen(string);
        m_data = new char[m_size];
        memcpy(m_data, string, m_size);
    }

    String(const String& other)
    {
        printf("Copied!\n");
        m_size = other.m_size;
        m_data = new char[m_size];
        memcpy(m_data, other.m_data, m_size);

    }
    
    /*String(String&& other)
    {
        printf("Moved!\n");
        m_size = other.m_size;
        m_data = other.m_data;
        
        other.m_size = 0;
        other.m_data = nullptr;

    }*/

 
    void printData()
    {
        for (uint32_t i = 0; i < m_size; i++)
        {
            printf("%c",m_data[i]);
        }
        printf("\n");
    }

    ~String()
    {
        printf("Destroyed!\n");
        delete m_data;
    }

private:
    char* m_data;
    uint32_t m_size;
};

class Entity
{
public:
    Entity(const String& name) :m_name(name){}

    //Entity(String&& name) :m_name(std::move(name)) //(String &&)name 使用强值转换同样可以
    //{}
    void print()
    {
        m_name.printData();
    }
private:
    String m_name;
};
int main()
{
    Entity entity("Wenhao");
    entity.print();
    system("pause");
   
}

输出结果如下:

Created!
Copied!
Destroyed!
Wenhao

使用字符串字面量去初始化对象,直接将内容赋给对象就好。但实际上会生成一个临时的String对象,先创建然后调用拷贝构造函数生成一个临时对象。相当于开辟了两次内存,复制两次。时间和空间效率低。

我们希望直接利用该字符串常量初始化对象,所以重载一个右值引用的构造函数,即上述代码中的注释部分。打开注释内容后的输出结果:

Created!
Moved!
Destroyed!
Wenhao

输出为Moved,没有进行复制内容。

3.2 重载移动复制运算符

#include<iostream>
using namespace std;

class String
{
public:
    String() = default;

    String(const char* string)
    {
        printf("Created!\n");
        m_size = strlen(string);
        m_data = new char[m_size];
        memcpy(m_data, string, m_size);
    }

    String(const String& other)
    {
        printf("Copied!\n");
        m_size = other.m_size;
        m_data = new char[m_size];
        memcpy(m_data, other.m_data, m_size);

    }
    
    String(String&& other) noexcept
    {
        printf("Moved!\n");
        m_size = other.m_size;
        m_data = other.m_data;
        
        other.m_size = 0;
        other.m_data = nullptr;

    }
    
    String& operator=(String&& other) noexcept
    {
        printf("Moved=!\n");
        if (this != &other)
        {
            delete m_data;
            m_size = other.m_size;
            m_data = other.m_data;

            other.m_size = 0;
            other.m_data = nullptr;
        }
        else
            return *this;
    }
 
    void printData()
    {
        for (uint32_t i = 0; i < m_size; i++)
        {
            printf("%c",m_data[i]);
        }
        printf("\n");
    }

    ~String()
    {
        printf("Destroyed!\n");
        delete m_data;
    }

private:
    char* m_data;
    uint32_t m_size;
};


int main()
{
    String a = "apple";
    String b;

    cout << "a:";
    a.printData();
    cout << "b:";
    b.printData();

    b = std::move(a);

    cout << "a:";
    a.printData();
    cout << "b:";
    b.printData();

    system("pause");
   
}

输出结果如下:

Created!
a:apple
b:
Moved=!
a:
b:apple

std::move() 能够将变量转为临时变量,即能将该变量的内容移动到=号左端的变量。

视频内容可参考下面链接:

【90】【Cherno C++】【中字】stdmove与移动赋值操作符_哔哩哔哩_bilibili

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值