首页 > 编程语言 >C++ 拷贝构造函数语义与移动构造函数语义

C++ 拷贝构造函数语义与移动构造函数语义

时间:2024-08-05 23:28:50浏览次数:17  
标签:自定义 语义 C++ MyClass 拷贝 移动 public 构造函数

拷贝构造函数语义与移动构造函数语义

一、拷贝构造函数语义

拷贝构造函数用于复制对象。当一个对象被复制时,编译器会根据特定条件生成默认的拷贝构造函数。

1. 编译器生成拷贝构造函数的条件
  1. 类中有类类型成员且没有自定义拷贝构造函数

    • 示例

      class CTB {
      public:
          CTB() {}
          CTB(const CTB& other) { /* 复制逻辑 */ }
      };
      
      class A {
      public:
          CTB member; // CTB类型成员
      };
      
      // A的拷贝构造函数会自动生成
      A a1;
      A a2 = a1; // 调用A的拷贝构造函数
      
  2. 子类没有自定义拷贝构造函数且父类有拷贝构造函数

    • 示例

      class CTB {
      public:
          CTB() {}
          CTB(const CTB& other) { /* 复制逻辑 */ }
      };
      
      class CTBSon : public CTB {
      public:
          // 没有自定义拷贝构造函数
      };
      
      // CTBSon的拷贝构造函数会自动生成
      CTBSon son1;
      CTBSon son2 = son1; // 调用CTBSon的拷贝构造函数
      
  3. 子类定义了虚函数

    • 示例

      class Base {
      public:
          virtual void func() {}
      };
      
      class CTBSon : public Base {
      public:
          // 没有自定义拷贝构造函数
      };
      
      // CTBSon的拷贝构造函数会自动生成
      CTBSon son1;
      CTBSon son2 = son1; // 调用CTBSon的拷贝构造函数
      
  4. 类中含有虚基类

    • 示例

      class VirtualBase {
      public:
          VirtualBase() {}
          VirtualBase(const VirtualBase& other) { /* 复制逻辑 */ }
      };
      
      class A : public VirtualBase {
      public:
          // 没有自定义拷贝构造函数
      };
      
      // A的拷贝构造函数会自动生成
      A a1;
      A a2 = a1; // 调用A的拷贝构造函数
      
2. 自定义拷贝构造函数

用户可以自定义拷贝构造函数以实现特定的复制逻辑。例如,可以选择深拷贝或浅拷贝。

class MyClass {
public:
    int* data;
    MyClass(int value) : data(new int(value)) {}
    
    // 自定义拷贝构造函数
    MyClass(const MyClass& other) {
        data = new int(*other.data); // 深拷贝
    }
    
    ~MyClass() {
        delete data;
    }
};
3. 深拷贝与浅拷贝
  • 浅拷贝:仅复制指针的值,两个对象指向同一内存地址,可能导致悬空指针或双重释放的问题。

    • 示例

      class ShallowCopy {
      public:
          int* data;
          ShallowCopy(int value) : data(new int(value)) {}
          
          // 浅拷贝的构造函数
          ShallowCopy(const ShallowCopy& other) : data(other.data) {}
          
          ~ShallowCopy() {
              delete data; // 可能导致双重释放
          }
      };
      
  • 深拷贝:复制指针所指向的数据,确保每个对象都有自己的独立副本。

4. 拷贝构造函数的使用场景
  • 对象传递:在函数参数中传递对象时,通常会调用拷贝构造函数。
  • 返回对象:当函数返回一个对象时,拷贝构造函数会被调用。
5. 拷贝赋值运算符

拷贝构造函数和拷贝赋值运算符的逻辑通常相似,但它们的使用场景不同。拷贝赋值运算符用于将一个对象的值赋给另一个已存在的对象。

6. 规则五(Rule of Five)

如果自定义了拷贝构造函数,通常也需要自定义移动构造函数、拷贝赋值运算符、移动赋值运算符和析构函数,以确保资源管理的正确性。确保它们的实现不冲突,并且正确管理资源。


二、移动构造函数语义

移动构造函数用于转移对象的资源。编译器生成移动构造函数的条件更加严格。

1. 禁止生成移动构造函数
  • 条件:如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或析构函数(任意一个),则编译器不会为该类生成移动构造函数和移动赋值运算符。

  • 示例

    class MyClass {
    public:
        MyClass() {}
        MyClass(const MyClass& other) { /* 复制逻辑 */ } // 自定义拷贝构造函数
    };
    
    // MyClass不会生成移动构造函数
    MyClass obj1;
    MyClass obj2 = std::move(obj1); // 错误:没有移动构造函数
    
2. 生成移动构造函数的条件
  • 条件:只有在类未定义任何自定义版本的拷贝构造函数、拷贝赋值运算符和析构函数时,且类的每个非静态成员都可以移动,编译器才会为该类合成移动构造函数和移动赋值运算符。

  • 示例

    class Movable {
    public:
        Movable() {}
        Movable(Movable&& other) noexcept { /* 移动逻辑 */ }
    };
    
    class MyClass {
    public:
        Movable member; // 可移动
        // 没有自定义拷贝构造函数、拷贝赋值运算符和析构函数
    };
    
    // MyClass会生成移动构造函数
    MyClass obj1;
    MyClass obj2 = std::move(obj1); // 正常调用移动构造函数
    
3. 成员可以移动的定义
  • 内置类型:如整型、浮点型等的成员变量可以移动。

  • 类类型:如果成员变量是一个类类型,并且该类有对应的移动构造函数或移动赋值运算符,则该成员变量可以移动。

  • 示例

    class Movable {
    public:
        Movable() {}
        Movable(Movable&& other) noexcept { /* 移动逻辑 */ }
    };
    
    class NonMovable {
    public:
        NonMovable() {}
        NonMovable(const NonMovable& other) { /* 复制逻辑 */ }
    };
    
    class MyClass {
    public:
        Movable movableMember; // 可移动
        NonMovable nonMovableMember; // 不可移动
        // 由于存在不可移动的成员,MyClass不会生成移动构造函数
    };
    
    // MyClass不会生成移动构造函数
    MyClass obj1;
    MyClass obj2 = std::move(obj1); // 错误:没有移动构造函数
    

三、总结

  • 拷贝构造函数主要用于复制对象,编译器会在特定条件下自动生成,确保对象的成员正确复制。
  • 移动构造函数用于转移对象的资源,编译器的生成条件更为严格,主要依赖于类是否有自定义的拷贝相关函数和成员的可移动性。

四、补充内容

1. 拷贝与移动的区别
  • 拷贝:创建一个对象的副本,所有成员都被复制,原对象和副本相互独立。
  • 移动:转移资源的所有权,原对象的资源被转移到新对象,原对象处于有效但未定义的状态。
2. 使用场景
  • 拷贝构造函数适用于需要保留原对象状态的情况,例如在需要多个相同对象的场景。
  • 移动构造函数适用于临时对象或资源管理,能够减少不必要的内存开销,提高性能。
3. 实践建议
  • 尽量使用移动语义以提高性能,尤其是在处理大型对象或动态分配资源时。
  • 在自定义类时,如果定义了拷贝构造函数,考虑是否也需要定义移动构造函数,以便在需要时能够高效地转移资源。

标签:自定义,语义,C++,MyClass,拷贝,移动,public,构造函数
From: https://blog.csdn.net/qq_68194402/article/details/140938409

相关文章

  • Codeforces Round 963 (Div. 2) A - C 详细题解(思路加代码,C++,Python) -- 来自灰名
    比赛链接:Dashboard-CodeforcesRound963(Div.2)-Codeforces之后有实力了再试试后面的题目,现在要做那些题,起码要理解一个多小时题目A:链接:Problem-A-Codeforces题目大意理解:        极少数不考翻译能读懂的cf题目(bushi)每个测试用例第一行一个n,......
  • C++_Const的相关知识点
    1.修饰函数参数,表示函数不会修改参数当const修饰函数参数时,表示函数内部不会修改该参数的值。这样做可以使代码更加安全,避免在函数内部无意中修改传入的参数值。尤其是引用作为参数时,如果确定不会修改引用,那么一定要使用const引用。voidfunc(constinta){......
  • C++ exe程序内存占用分析
    编译 $gitclonehttps://github.com/google/bloaty$cdbloaty$cmake-Bbuild-GNinja-S.$cmake--buildbuild$cmake--buildbuild--targetinstall命令bloaty.exe --list-sourcesrmembersthe.ofilesina.afilecompileunitssourcefile......
  • 【C++ 面试 - 基础题】每日 3 题(一)
    ✍个人博客:Pandaconda-CSDN博客......
  • 【C++】实验十三
    题目:1、声明Point(点)类,由Point类派生出Circle(圆)类,再由Circle类派生出Cylinder(圆柱)类。将类的定义部分分别作为3个头文件。对它们的成员函数的声明部分分别作为3个源文件(.cpp文件),在主函数中用#include命令把它们包含进来。形成一个完整的程序,并上机运行。2、教材第12章例12.3的......
  • L2-008 最长对称子串 C++
    对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定IsPAT&TAPsymmetric?,最长对称子串为sPAT&TAPs,于是你应该输出11。输入格式:输入在一行中给出长度不超过1000的非空字符串。输出格式:在一行中输出最长对称子串的长度。输入样例:IsPAT&TAPsymmetric?输出样......
  • 在python jupyter下运行cuda c++程序
    Installrunthisonjupyter(*.ipynb)files!pip3installnvcc4jupyterUsageloadtheextensiontoenablethemagiccommands:%load_extnvcc4jupyterRuncudatest%%cuda#include<stdio.h>__global__voidhello(){printf("Hellofromblock......
  • 在python jupyter下运行cuda c++程序
    Installrunthisonjupyter(*.ipynb)files!pip3installnvcc4jupyterUsageloadtheextensiontoenablethemagiccommands:%load_extnvcc4jupyterRuncudatest%%cuda#include<stdio.h>__global__voidhello(){printf("Hellofromblock......
  • C++类和对象
    文章目录C++类和对象类和对象1.类和对象的基本概念注意事项:2.类的定义和成员注意事项:3.构造函数和析构函数注意事项:4.访问控制和封装注意事项:5.this指针注意事项:6.静态成员注意事项:7.常量成员函数和常量对象注意事项:8.友元函数和友元类注意事项:9......
  • 《Advanced RAG》-05-探索语义分块(Semantic Chunking)
    摘要文章首先介绍了语义分块在RAG中的位置和作用,并介绍了常见的基于规则的分块方法。然后,阐述了语义分块的目的是确保每个分块包含尽可能多的独立语义信息。接着,文章分别介绍了三种语义分块方法的原理和实现方法,并对每种方法进行了总结和评估。文章观点语义分块是R......