• C语言日记 35 拷贝构造函数


    书P132:

    拷贝构造函数的作用是

    用已存在的对象初始化另一对象,两对象类类型应一样

    在这里我们可以看到,

    他对被拷贝的对象的要求只有“已存在的对象,两对象类类型一样”,也就是说他这里也没有说我们不能跨区域(类)拷贝

    那我们可不可以在不同的类中拷贝别的对象呢?(例如在A类里面拷贝B类的某一对象)

    附:

    提出这个问题的原因是因为这段话下面马上就有一句话“拷贝构造函数的形参是本类对象的引用”

    那么我想他这么写是不是已经在明示暗示我们不可以在不同的类中拷贝别的对象了呢?

    书P132拷贝构造函数举例:

    类部分(类名和类体):

    1. class Point
    2. {
    3. public:
    4. Point(int xx = 0, int yy = 0)
    5. //内联构造函数
    6. {
    7. X = xx; Y = yy;
    8. }
    9. Point(Point& p);//拷贝构造函数
    10. int GetX() { return X; }
    11. int GetY() { return Y; }
    12. private:
    13. int X, Y;
    14. };

    类外(构造函数定义实现部分):

    1. Point::Point(Point& p)
    2. {
    3. X = p.X;
    4. Y = p.Y;
    5. cout << "拷贝构造函数被调用" << endl;
    6. }

    如果我们这里什么也不写,也就是说调用的是默认拷贝构造函数,除了在拷贝构造函数中不会写

    cout的语句之外,其他的函数体和这里书上给出的函数体,一模一样

    其中这里的cout语句和原内联构造函数不一样(原函数里面没有),其目的在于:

    这样后面每次当我们的程序里面调用了拷贝构造函数时,每调用一次,我们就可以看到结果输出一次这个语句,而且我们还可以具体的看到具体的语句输出在哪里(什么时候被输出即拷贝构造函数在什么时候被调用)

    合并并补充完整:

    1. #include
    2. using namespace std;
    3. class Point
    4. {
    5. public:
    6. Point(int xx = 0, int yy = 0)
    7. //内联构造函数
    8. {
    9. X = xx; Y = yy;
    10. }
    11. Point(Point& p);//拷贝构造函数
    12. int Getx() { return X; }
    13. int GetY() { return Y; }
    14. private:
    15. int X, Y;
    16. };
    17. Point::Point(Point& p)
    18. {
    19. X = p.X;
    20. Y = p.Y;
    21. cout << "拷贝构造函数被调用" << endl;
    22. }
    23. int main()
    24. {
    25. }

    (1):(拷贝赋值,把对象A的值拷贝赋给对象B)

    1. int main()
    2. {
    3. Point A(6, 8); //自动调用构造函数
    4. Point B(A); //自动调用拷贝构造函数
    5. cout << B.GetX() << endl;
    6. return 0;
    7. }

    完整:(后面同理,不再赘述)

    1. #include
    2. using namespace std;
    3. class Point
    4. {
    5. public:
    6. Point(int xx = 0, int yy = 0)
    7. //内联构造函数
    8. {
    9. X = xx; Y = yy;
    10. }
    11. Point(Point& p);//拷贝构造函数
    12. int GetX() { return X; }
    13. int GetY() { return Y; }
    14. private:
    15. int X, Y;
    16. };
    17. Point::Point(Point& p)
    18. {
    19. X = p.X;
    20. Y = p.Y;
    21. cout << "拷贝构造函数被调用" << endl;
    22. }
    23. int main()
    24. {
    25. Point A(6, 8); //自动调用构造函数
    26. Point B(A); //自动调用拷贝构造函数
    27. cout << B.GetX() << endl;
    28. return 0;
    29. }

    结果:

    039be7c3b07b42bfb7081cecd81cc6db.png

    另外,对于验证:

    如果我们这里什么也不写,也就是说调用的是默认拷贝构造函数,除了在拷贝构造函数中不会写

    cout的语句之外,其他的函数体和这里书上给出的函数体,一模一样

    的说法的证明的程序如下:

    1. #include
    2. using namespace std;
    3. class Point
    4. {
    5. public:
    6. Point(int xx = 0, int yy = 0)
    7. //内联构造函数
    8. {
    9. X = xx; Y = yy;
    10. }
    11. int GetX() { return X; }
    12. int GetY() { return Y; }
    13. private:
    14. int X, Y;
    15. };
    16. int main()
    17. {
    18. Point A(6, 8); //自动调用构造函数
    19. Point B(A); //自动调用拷贝构造函数
    20. cout << B.GetX() << endl;
    21. return 0;
    22. }

    结果:

    87ccd11ab64f4fdb9d0828b0c6d530b4.png

     后面也同理,不再赘述,只标注“使用默认拷贝构造函数结果”;

    (2):(把实参A的值拷贝赋给形参p)

    1. void fun1(Point p)//形参为类对象
    2. {
    3. cout << p.GetX() << ',' << p.GetY() << endl;
    4. }
    5. int main()
    6. {
    7. Point A(1, 2);
    8. fun1(A);
    9. return 0;
    10. }

    结果:

    9e834481ed8d45f4a5e65e158d59fbfd.png

    使用默认拷贝构造函数结果:

    bab130006a6645808a200869368c5390.png


    (3):返回值为类对象,系统自动把返回值拷贝到一个临时的无名对象中(详见书P133)

    1. Point fun2()
    2. {
    3. Point A(1, 2);
    4. return A;
    5. }
    6. int main()
    7. {
    8. Point B;
    9. B = fun2();
    10. return 0;
    11. }

    结果:

    8031f5117a21484da8e8e7a0daf34f24.png

    使用默认拷贝构造函数结果:
    4f68a0dcd6314ff498ca5a69ebd19033.png

    例8-11 浅拷贝异常案例。

    源程序:

    1. #include
    2. using namespace std;
    3. class Rect
    4. {
    5. private:
    6. int width;
    7. int height;
    8. int* p;//指针成员
    9. public:
    10. Rect()//构造函数,p指向堆中分配的空间
    11. {
    12. p = new int(100);
    13. cout << "calling copy constructor!" << endl;
    14. }
    15. ~Rect() //析构函数,释放动态分配的空间
    16. {
    17. cout << "destructor is called!" << endl;
    18. delete p;
    19. }
    20. };
    21. int main()
    22. {
    23. Rect rect1;
    24. Rect rect2(rect1);//复制对象
    25. return 0;
    26. }

    运行结果:

    calling copy constructor!

    destructor is called!

    destructor is called!

    539cd7a561b14d5fa724aee1bf7854f3.png

     3f81458258b64502b27a0e326d4ca3f0.png

    像(在)这里开始,他才真正开始默认我们已经懂得和学过(默认)拷贝构造函数,不用在程序里面具体去写一遍默认拷贝构造函数的函数声明和函数体,全部用默认(隐式)拷贝构造函数;

    而如果我们还没学过拷贝构造函数,由于前面构造函数的函数体看起来和往常根本没有任何区别,而后面他一下子就开始用起拷贝构造函数,不懂的话自然看着就懵了

    另外,我们自己在判断程序的时候,不妨平时就把构造函数与拷贝构造函数捆绑在一起作为一个整体,简单的说,就是看到一个构造函数自动知道在其旁边就有着对应的拷贝构造函数

    例8-12 深拷贝的使用。

    源程序:

    1. #include
    2. using namespace std;
    3. class Rect
    4. {
    5. private:
    6. int width;
    7. int height;
    8. int* p; //指针成员
    9. public:
    10. Rect()//构造函数,p指向堆中分配的空间
    11. {
    12. p = new int(100);
    13. cout << "calling copy constructor!" << endl;
    14. }
    15. Rect(const Rect& r)
    16. {
    17. width = r.width;
    18. height = r.height;
    19. p = new int;//为新对象重新动态分配空间
    20. *p = *(r.p);
    21. }
    22. ~Rect()//析构函数,释放动态分配的空间
    23. {
    24. cout << "destructor is called!" << endl;
    25. delete p;
    26. }
    27. };
    28. //
    29. int main()
    30. {
    31. Rect rect1;
    32. Rect rect2(rect1);//复制对象
    33. return 0;
    34. }

    结果:

    4f63bfac46944458961606eb9b1cfa4a.png

    而这里这个程序和前一个示例的区别,(也就是深拷贝和浅拷贝的区别),(也)就是增加了一段给新的被粘贴的对象新写(创建)一个内存空间:

    1. Rect(const Rect& r)
    2. {
    3. width = r.width;
    4. height = r.height;
    5. p = new int;//为新对象重新动态分配空间
    6. *p = *(r.p);
    7. }

     其中const(我记得前面好像写过关于这个的解释,但是具体在哪里已经找不得了)的意思和具体作用,简单来说就是保证程序段只读不写(只能够读取,不能够修改)

    详细情况和解释,见

    69 结构体-结构体中const使用场景_哔哩哔哩_bilibili

    另外,对于其在成员函数中的作用的补充,详见:35 类和对象-对象特性-const修饰成员函数_哔哩哔哩_bilibili

    另外,如果要写得更简单简洁,也可以这样写:

    1. Rect(const Rect& r)
    2. {
    3. width = r.width;
    4. height = r.height;
    5. p = new int(*r.p);
    6. //p = (r.p);
    7. //编译器实际上默认实现的就是这个代码
    8. }

    该段深拷贝浅拷贝的具体内容,详见:

    27 类和对象-对象特性-深拷贝与浅拷贝_哔哩哔哩_bilibili

    另外,为了巩固这块内容,(实际上是因为我们想达到一个深拷贝以后还能把两个对象里面的具体数据输出的效果)我们这里再把程序补充完整为处处对象具体内容同的程序:

    1. #include
    2. using namespace std;
    3. class Rect
    4. {
    5. private:
    6. int width;
    7. int height;
    8. int* p; //指针成员
    9. public:
    10. Rect()//构造函数,p指向堆中分配的空间
    11. {
    12. p = new int(100);
    13. }
    14. Rect(const Rect& r)
    15. {
    16. cout << "calling copy constructor!" << endl;
    17. width = r.width;
    18. height = r.height;
    19. p = new int(*r.p);
    20. //p = (r.p);
    21. //编译器实际上默认实现的就是这个代码
    22. }
    23. ~Rect()//析构函数,释放动态分配的空间
    24. {
    25. cout << "destructor is called!" << endl;
    26. delete p;
    27. }
    28. int display()
    29. {
    30. cout << "指针指向" << *p <<
    31. "; 高度" << height << "; 宽度" << width << endl;
    32. return 1;
    33. }
    34. };
    35. //
    36. int main()
    37. {
    38. Rect rect1;
    39. Rect rect2(rect1);//复制对象
    40. cout << rect1.display() << endl;
    41. cout << rect2.display() << endl;
    42. return 0;
    43. }

    结果:

     改动之处:

    • 把显示输出调用拷贝构造函数的句子放到了拷贝构造函数中,而不是构造函数里面
    • 新写了display()函数,用于输出这几个对象里面的具体内容

    只得一提的是:

    我们原来一开始本来打算写两种形式的display()函数:

    一种采用cout语句输出值,另一种采用return语句返回值

    然而,当我们一开始只写cout语句的display()函数时:

    1. int display()
    2. {
    3. cout << "指针指向" << *p <<
    4. "; 高度" << height << "; 宽度" << width << endl;
    5. }

    结果:

    所以在这里我们可以知道:

    在类中定义的函数必须有一个返回值

    但是总感觉这好像又不对吧,例如:C语言日记 32 类的对象,this指针_宇 -Yu的博客-CSDN博客

    的 例8-6,这里面的

    1. void Time::Get_Time()//定义公有成员函数
    2. {
    3. cout << Hour << ":" << Minute << ":" << Second << endl;
    4. }

    好像也没有写返回的值啊??

    另外,如果我们采用return语句返回值,则display()函数为:

    (1):

    1. int display()
    2. {
    3. return *p ;
    4. return height;
    5. return width;
    6. }

    结果:

     (2):

    1. int display()
    2. {
    3. return height;
    4. return *p ;
    5. return width;
    6. }

    结果:

    (所以)那么这里我们就可以说,

    当我们有多个返回值,return语句的时候,无论我们有多少个返回值,最终只输出第一个返回值(输出只输出第一个返回值,返回是不是只有第一个返回值(那)我就不知道了)

  • 相关阅读:
    Adobe 认证证书怎么考
    【附源码】计算机毕业设计SSM实验室设备管理系统
    人工神经网络的典型模型,2020最新神经网络模型
    缓存一致性
    2.1.1 面向对象:类和对象概念
    Docker 实用操作文档
    CCF CSP认证历年题目自练Day45
    【微信小程序开发】宠物预约医疗项目实战-登录实现
    《向量数据库指南》——AI原生向量数据库Milvus Cloud 2.3架构升级
    秋季开学必备数码好物推荐,大学生开学必备电子产品推荐
  • 原文地址:https://blog.csdn.net/Zz_zzzzzzz__/article/details/127822774