首页 > 其他分享 >左值引用与右值引用

左值引用与右值引用

时间:2023-03-15 11:15:21浏览次数:28  
标签:std 右值 int 左值 引用 构造函数

左值引用和右值引用的区别?右值引用的意义

  • 左值引用是对左值的引用,右值引用是对右值的引用

    • 左值右值的概念

      • 左值:可以在等号左边,能够取地址,并且具备名字的(左值可以放在右边,只要能够放在等号左边就是左值)(const左值引用能指引右值,局限是不能修改这个值)

        • int i = 0;//运行流程是i+1之后被赋值10
          ++i = 10; //表达式的结果是左值引用,指向 i 的内存地址
          
        • (i=9) = 100;//(i=9)可以做为做值,能够取到地址,且具备名字
          (i+=10) = 1000;//(i+=10)可以作为左值
          
        • 解引用(操作符是* 例如:*ptr。解引用是指将指针转换为实际值或对象的过程。)也可以作为左值

           int* ptr = new int(10);
           *ptr = 20; // 解引用作为左值
          
      • 右值只能在等号右边,不能够取地址,不具备名字的(关键是如何区分右值)(右值指的是不具备“可修改状态”的表达式)

        • 纯右值:字面值

          int getValue() {return 10;}
          int main() {int a = getValue();  // 右值赋值到左值
             			 return 0;}
          ---------------------------------------------------
          void myFunction(int&& value) {// do something}
          int main() {myFunction(getValue());  // 右值作为函数参数
              		return 0;}    
          //在上述代码中,myFunction()函数的参数类型为int&&,表示该参数为右值引用类型,可以接受值作为参数。
          
          
          • int i = 10,这个10是纯右值

          • 返回非引用类型的函数调用

          • 后置自增/自减

            • int x = 10;
              int y = x++; // x 先赋值给 y,再进行自增
              //这里的 x++ 表示先将 x 的值赋给 y,然后再将 x 自增,由于自增操作返回的是自增前的值,因此 x++ 是一个右值
              //举个反例子int y = ++x;
              //其中++x是一个左值引用    
              
        • 将亡值:c++11引入的一个与右值引用(移动语义:移动语义是指通过移动资源的所有权而不是复制资源,来提高代码性能的C++语言特性)相关的值类型

        • std::vector<int> createVector() {
          	std::vector<int> v = {1, 2, 3};
              return v;}
          int main() {
              std::vector<int> v2 = createVector(); // 这里会调用移动构造函数
              return 0;}
          //在createVector()函数中,我们创建了一个std::vector<int>类型的局部变量v,并在函数结束时将其返回。由于返回值是一个临时变量,这个临时变量在调用函数结束时就会被销毁。因此,在将其返回时,编译器会自动使用移动构造函数将其转换为一个将亡值,以便在函数调用结束时将其移动到调用方的变量v2中。
          
          • 用将亡值来触发移动构造注1/移动赋值构造并进行资源转移,之后将调用析构函数注2
      • 左值右值的区别

  • 左值引用

    • 避免对象的拷贝,可以直接使用内容
    • const左值引用能指引右值,局限是不能修改这个值
    • 声明出来的左值引用或者右值引用都是左值
  • 右值引用

    • 意义:
      • 实现移动语义
      • 实现完美转发
    • 通过std::move()实现指向左值

声明出来的左值引用或者右值引用都是左值,左值引用是对左值的引用,右值的引用是对右值的引用,两者都是引用,声明出来的变量都是左值,比如说int &a和int &&a中的a都是左值。const左值引用可以指向右值,const int &a就可以引用右值,但是不能修改这个值,因此需要右值引用解决这个问题右值引用也可以指向左值,可以通过move函数指向这个左值。


注1移动构造函数

​ 移动构造是一种特殊的构造函数,用于将一个对象的资源(比如内存)转移到另一个对象,同时避免不必要的内存拷贝,从而提高程序性能。通常情况下,移动构造函数会接受一个将亡值(即将被销毁的对象),并将其资源转移到一个新的对象中,然后将将亡值的指针置为null,避免其析构函数再次释放资源。

class MyString {
public:
    // 构造函数
    MyString(const char* str) {
        std::size_t size = std::strlen(str) + 1;
        data_ = new char[size];
        std::strcpy(data_, str);
    }

    // 移动构造函数
    MyString(MyString&& other) {
        data_ = other.data_;
        other.data_ = nullptr;
    }

    // 析构函数
    ~MyString() {
        delete[] data_;
    }

private:
    char* data_;
};

//在使用该类的过程中,可能会发生对象的拷贝,如下所示:
MyString s1("Hello");
MyString s2(s1); // 拷贝构造
//这时候,由于 s1 和 s2 都有自己的数据存储,因此需要进行数据的复制,这样就浪费了一部分时间和内存空间。而如果使用移动构造函数,则可以直接将 s1 的数据指针 data_ 赋给 s2,避免了数据的复制:
MyString s1("Hello");
MyString s2(std::move(s1)); // 移动构造
//在移动构造的过程中,由于 s1 的数据指针 data_ 已经被转移给了 s2,因此 s1 的析构函数就不再需要删除 data_ 了,避免了重复删除已经被释放的内存空间。

构造函数:

​ 构造函数是一种特殊的成员函数,用于创建和初始化对象。当定义一个对象时,构造函数会自动调用来初始化对象的数据成员。

class MyClass {
public:
    int x;
    // 构造函数
    MyClass(int val) {
        x = val;
        std::cout << "MyClass object is created with x = " << x << std::endl;
    }
};
int main() {
    MyClass obj(10); // 使用构造函数创建对象

    return 0;
}

//在上面的示例中,MyClass 类有一个带有一个整数参数的构造函数,用于初始化类中的数据成员 x。在 main 函数中,我们创建了一个名为 obj 的 MyClass 类型的对象,并将值 10 传递给构造函数。,这表明构造函数被成功调用,并使用传递的值 10 初始化了 x 数据成员。

构造函数的作用可以总结为以下几点:

  1. 初始化对象的成员变量,确保对象在创建后的初始状态是正确的。
  2. 分配内存或资源,并对其进行初始化,如动态分配内存、打开文件、连接网络等。
  3. 对象创建时执行一些必要的操作,如日志记录、统计等。
  4. 可以提供默认参数,方便用户使用。

注2

​ 析构函数的作用是在对象被销毁时自动执行清理操作,如释放对象所占用的内存等。当一个对象的生命周期结束时,C++编译器会自动调用其析构函数。

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor called" << std::endl;
    }

    ~MyClass() {
        std::cout << "MyClass destructor called" << std::endl;
    }
};

int main() {
    MyClass myObj;
    return 0;
}
//在上述代码中,我们定义了一个名为MyClass的类,其中包含了一个构造函数和一个析构函数。在main函数中,我们创建了一个MyClass对象myObj,并在程序结束时销毁它。由于MyClass包含了一个析构函数,因此在myObj被销毁时,其析构函数会自动被调用,输出一条提示信息。

标签:std,右值,int,左值,引用,构造函数
From: https://www.cnblogs.com/LaChlanYang/p/17217775.html

相关文章

  • 11、指针和引用
    指针:是一个变量,存储一个变量的地址。引用:是变量的别名。1、初始化指针定义时不必初始化,引用必须初始化。指针初始化时可为NULL,引用不能初始化为NULL。inta=1......
  • Java中的基本数据类型和引用数据类型
    一、基本数据类型:byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-128~127,默认值0short:短整型,在内存中占16位,即2个字节,取值范围-32768~32767,默认值0int:整......
  • 关于AWS中IAM-Certificate-证书被那些资源所引用的查询方法
    在AWS中管理证书的地方目前有ACM和IAM两个地方,笔者推荐还是尽量使用ACM证书,优点在下文中可以看到但是有时候又必须得使用IAM证书、如CloudFront中只能支持使用IAM中的证书......
  • maven引用本地jar包并打包
    1、resources下建目录lib,lib下放jar包 2、pom文件加依赖<dependency><groupId>包名</groupId><artifactId>k3cloud-webapi-sdk</artifactId......
  • jpa 循环引用
    循环引用就是:A中有B,B中有A。一查询,就无限递归了。现在给出解决方案(以及我认为,最好的方案。)1@JsonIgnore直接忽略对象,简单粗暴,变成单项关联了。 2@Transient简单粗......
  • C++类内定义引用成员
    遵循三个规则C++类内可以定义引用成员变量,但要遵循以下三个规则:不能用默认构造函数初始化,必须提供构造函数来初始化引用成员变量。否则会造成引用未初始化错误。构造函......
  • 变量的引用
    原文链接:https://blog.csdn.net/ChaoFeiLi/article/details/103593740定义:变量的引用是变量的别名注意点:变量的引用在声明时必须初始化变量的引用作为变量的别名,因此......
  • 工程化引用数据的几个类型总结
    基本数据类型有string,number,Boolean,undefined,null和es6新增的symbol和bingint。引用数据类型有数组、对象、字符串。在es6新增的还有set、map、weakset、weakmap。symbol创......
  • java8新特性-引用流-sorted
    例子:List<User>users=newArrayList<>();users.add(newUser("张三",30));users.add(newUser("李四",34));users.add(newUser("王五",20));......
  • java8新特性-引用流-max,min
    例子:List<User>users=newArrayList<>();users.add(newUser("张三",30));users.add(newUser("李四",34));users.add(newUser("王五",20));......