• C++对象模型(14)-- 构造函数语义学:拷贝构造函数和赋值运算赋


    1、拷贝构造函数

    1.1 什么是拷贝构造函数

    拷贝构造函数是一种构造函数,它的功能是创建新对象。也就是说对象还没生成,这时利用另一个对象的拷贝来生成新的对象。

    1. class MyDemo {
    2. public:
    3. // 默认构造函数
    4. MyDemo(){}
    5. // 拷贝构造函数
    6. MyDemo(const MyDemo& _demo) {
    7. cout << "copy constructor is called" << endl;
    8. }
    9. };
    10. int main()
    11. {
    12. MyDemo demoA;
    13. MyDemo demoB = demoA;
    14. return 0;
    15. }

    例子中的:MyDemo demoB = demoA; 语句调用的就是拷贝构造函数。

    1.2、默认拷贝构造函数生成规则

    (1)包含一个类类型的成员变量,且成员变量所属的类有拷贝构造函数。

    (2)其父类有拷贝构造函数。

    (3)类有虚函数。

    (4)类继承虚基类。

    这几条生成规则,跟默认构造函数的生成规则相似。因为拷贝构造函数也是一种构造函数,这几条规则的出发点是为了保证类对象的完整性。

    1.3 用到拷贝构造函数的3种场景

    (1)用 = 号直接赋值

    1. int main()
    2. {
    3. MyDemo demoA;
    4. MyDemo demoB = demoA;
    5. return 0;
    6. }

    (2)函数参数传递

    所以拷贝构造函数必须以引用的方式传递参数。这是因为,在值传递的方式传递给一个函数的时候,会调用拷贝构造函数生成函数的实参。如果拷贝构造函数的参数仍然是以值的方式,就会无限循环的调用下去,直到函数的栈溢出。

    1. void test(MyDemo _demo){ }
    2. int main()
    3. {
    4. MyDemo demoA;
    5. MyDemo demoB;
    6. demoB.test(demoA);
    7. return 0;
    8. }

    (3)函数返回值

    1. MyDemo getDemo() {
    2. MyDemo demo;
    3. return demo;
    4. }
    5. int main()
    6. {
    7. MyDemo demoA;
    8. demoA.getDemo();
    9. return 0;
    10. }

    (4)用花括号列表初始化一个数组中的元素

    1. int main()
    2. {
    3. MyDemo demoA;
    4. MyDemo demoArr = { demoA };
    5. return 0;
    6. }

    (5)标准库容器调用insert或push添加成员时

    2、赋值运算符

    当对象都已经生成,把另一个对象赋值给这个对象时,会调用赋值运算符。

    1. class MyDemo {
    2. public:
    3. // 默认构造函数
    4. MyDemo(){}
    5. // 拷贝构造函数
    6. MyDemo(const MyDemo& _demo) {
    7. cout << "copy constructor is called" << endl;
    8. }
    9. // 赋值运算符
    10. MyDemo& operator = (const MyDemo& _demo) {
    11. cout << "operator = is called" << endl;
    12. return *this;
    13. }
    14. };
    15. int main()
    16. {
    17. MyDemo demoA;
    18. MyDemo demoB;
    19. demoB = demoA;
    20. return 0;
    21. }

    调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象产生。如果产生了新的对象,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。

    3、浅拷贝和深拷贝

    3.1 浅拷贝

    当一个类中有指针变量时,如果只拷贝了指针的值,而指针所指向的地址并没有拷贝过来,这种拷贝就叫做浅拷贝。

    1. class MyDemo {
    2. public:
    3. MyDemo() {
    4. pm_i = new int(3);
    5. }
    6. ~MyDemo() {
    7. delete pm_i;
    8. }
    9. int* pm_i;
    10. };
    11. int main()
    12. {
    13. MyDemo demoA;
    14. MyDemo demoB(demoA);
    15. return 0;
    16. }

    运行上面的代码,发现会报错。为什么呢?

    因为对象demoB和demoA的指针pm_i都指向了同一个地址,这样的话指针pm_i所指向的地址会被delete两次,所以就会报错。

    3.2 深拷贝

    当类含有指针变量时,在拷贝构造函数中应该为指针变量重新申请一块内存空间,并把相应的值拷贝到该内存中,这种拷贝就叫深拷贝。

    1. MyDemo(const MyDemo& _demo) {
    2. pm_i = new int(5);
    3. memcpy(pm_i, _demo.pm_i, sizeof(int));
    4. }

  • 相关阅读:
    【PyTorch】基础知识及常用操作
    Spring Security认证之基本认证
    【批处理DOS-CMD命令-汇总和小结】-cmd扩展命令、扩展功能(cmd /e:on、cmd /e:off)
    荣耀回应强迫员工购买股份:不实消息;​微软为Win11 引入云操作系统;苹果定于6月6日举行开发者大会 |极客头条
    单例模式 创建型模式之一
    LT8911EXB MIPI CSI/DSI转EDP信号转换
    【Qt图书管理系统】1.项目设计与需求分析
    小程序的数据驱动和vue的双向绑定有何异同?
    产品代码都给你看了,可别再说不会DDD(三):战略设计
    zookeeper3.8.0集群安装及基础命令
  • 原文地址:https://blog.csdn.net/mars21/article/details/133895358