• 【第二天】C++类和对象解析:构造函数、析构函数和拷贝构造函数的完全指南


    目录

    一、类的引出概述

    二、封装

    三、类的案例(了解)

    1、设计一个person类

    2、设计一个立方体类

    3、点和圆的关系

    4、类中成员函数在类外实现

    5、类在其他文件实现

    四、构造函数(初始化工作)

    1、概述

    2、创建

    3、分类

    4、调用

    5、explicit关键字防止构造函数隐式转换

    五、析构函数(清理工作)

    六、拷贝构造函数

    1、定义

    2、拷贝构造 和 无参构造 有参构造的关系

    3、拷贝构造调用形式

    4、拷贝构造中浅拷贝和深拷贝

     七、初始化列表

     八、对象数组


     

    一、类的引出概述

            在c语言结构体中,行为和属性是分开的,万一调用错误,将会导致问题发生。c++中类将数据方法封装在一起,加以权限区分,用户只能通过公共方法 访问 私有数据

    二、封装

            封装特性包含两个方面,一个是属性和变量合成一个整体,一个是给属性和函数增加访问权限

    1.把变量(属性)和函数(操作)合成一个整体,封装在一个类中

    2.对变量和函数进行访问控制访问权限
    3.在类的内部(作用域范围内),没有访问权限之分,所有成员可以相互访问

    4.在类的外部(作用域范围外),访问权限才有意义:.public、private、protected

    5.在类的外部,只有public.修饰的成员太能被访问,在没有涉及继承与派生时,private和protected是相同等级的.外部不允许访问        

    访问属性属性对象内部对象外部
    public公有可访问可访问
    private保护可访问不可访问
    protected私有可访问不可访问

    尽量设置成员变量为私有权限,将方法设置成公有。

    优点:

    对变量的设置时的控制

    实现变量设置只读权限

    实现变量设置只写权限

    实现变量设置可读可写权限

    struct和class的区别:class默认访问权限为.private,struct默认访问权限为public.

    三、类的案例(了解)

    1、设计一个person类

            设计一个Person类,Person类具有name和age属性,提供初始化函数(Init),并提供对name和age 的读写函数(set,get),但必须确保age的赋值在有效范围内(0-100),超出有效范围,则拒绝赋值,并 提供方法输出姓名和年龄。----需要六个函数

    1. #include <iostream>
    2. using namespace std;
    3. #include<string.h>
    4. class Person
    5. {
    6. private:
    7. char mName[32];
    8. int mAge;
    9. public:
    10. //初始化成员
    11. void init(char *name, int age)
    12. {
    13. strcpy(mName, name);
    14. if(age>=0 && age<=100)
    15. {
    16. mAge = age;
    17. }
    18. else
    19. {
    20. cout<<"年龄无效"<<endl;
    21. }
    22. return;
    23. }
    24. //设置name
    25. void setName(char *name)
    26. {
    27. strcpy(mName, name);
    28. }
    29. //获取name
    30. char *getName(void)
    31. {
    32. return mName;
    33. }
    34. //设置age
    35. void setAge(int age)
    36. {
    37. if(age>=0 && age<=100)
    38. {
    39. mAge = age;
    40. }
    41. else
    42. {
    43. cout<<"年龄无效"<<endl;
    44. }
    45. }
    46. //得到age
    47. int getAge(void)
    48. {
    49. return mAge;
    50. }
    51. //显示所有数据
    52. void showPerson(void)
    53. {
    54. cout<<mName<<" "<<mAge<<endl;
    55. }
    56. };
    57. void test02()
    58. {
    59. Person ob1;
    60. ob1.init("lucy", 18);
    61. ob1.showPerson();
    62. ob1.setName("bob");
    63. cout<<"年龄:"<<ob1.getAge()<<endl;
    64. ob1.showPerson();
    65. }

    2、设计一个立方体

            设计立方体类(Cube),求出立方体的面积( 2ab +2ac +2bc )和体积( a*b*c),分别用全局函数和成员函数判断两个立方体是否相等。

    1. class Cube
    2. {
    3. private:
    4. int mA;
    5. int mB;
    6. int mC;
    7. public:
    8. void setA(int a)
    9. {
    10. mA = a;
    11. }
    12. int getA(void)
    13. {
    14. return mA;
    15. }
    16. void setB(int b)
    17. {
    18. mB = b;
    19. }
    20. int getB(void)
    21. {
    22. return mB;
    23. }
    24. void setC(int c)
    25. {
    26. mC = c;
    27. }
    28. int getC(void)
    29. {
    30. return mC;
    31. }
    32. //获取面积
    33. int getS(void)
    34. {
    35. return (mA*mB+mB*mC+mC*mA)*2;
    36. }
    37. //获取体积
    38. int getV(void)
    39. {
    40. return mA*mB*mC;
    41. }
    42. //成员函数 比较两个立方体是否先等
    43. bool compareCube01(Cube &ob)
    44. {
    45. if(mA==ob.mA && mB ==ob.mB && mC == ob.mC)
    46. {
    47. return true;
    48. }
    49. return false;
    50. }
    51. };
    52. //全局函数 比较两个立方体是否先等
    53. bool compareCube02(Cube &ob1, Cube &ob2)
    54. {
    55. if(ob1.getA()==ob2.getA() && ob1.getB() ==ob2.getB() && ob1.getC() == ob2.getC())
    56. {
    57. return true;
    58. }
    59. return false;
    60. }
    61. void test()
    62. {
    63. Cube ob1;
    64. ob1.setA(10);
    65. ob1.setB(20);
    66. ob1.setC(30);
    67. cout<<"面积:"<<ob1.getS()<<endl;
    68. cout<<"体积:"<<ob1.getV()<<endl;
    69. Cube ob2;
    70. ob2.setA(10);
    71. ob2.setB(20);
    72. ob2.setC(30);
    73. // if(compareCube02(ob1, ob2))
    74. if(ob1.compareCube01(ob2))
    75. {
    76. cout<<"相等"<<endl;
    77. }
    78. else
    79. {
    80. cout<<"不相等"<<endl;
    81. }
    82. }
    83. int main(int argc, char *argv[])
    84. {
    85. test();
    86. return 0;
    87. }

    3、点和圆的关系

            设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。

    1. class Point
    2. {
    3. private:
    4. int mX;
    5. int mY;
    6. public:
    7. void setX(int x)
    8. {
    9. mX = x;
    10. }
    11. int getX(void)
    12. {
    13. return mX;
    14. }
    15. void setY(int y)
    16. {
    17. mY = y;
    18. }
    19. int getY(void)
    20. {
    21. return mY;
    22. }
    23. };
    24. class Circle
    25. {
    26. private:
    27. Point p;//对象作为类的成员变量
    28. int mR;
    29. public:
    30. void setPoint(int x, int y)
    31. {
    32. p.setX(x);
    33. p.setY(y);
    34. }
    35. Point getPoint(void)//打印圆点
    36. {
    37. return p;
    38. }
    39. void setR(int r)
    40. {
    41. mR = r;
    42. }
    43. int getR(void)
    44. {
    45. return mR;
    46. }
    47. //判断点 在圆的位置
    48. int pointIsOnCircle(Point &ob)
    49. {
    50. int len = (ob.getX()-p.getX())*(ob.getX()-p.getX())+\
    51. (ob.getY()-p.getY())*(ob.getY()-p.getY());
    52. if(len == mR*mR)
    53. {
    54. return 0;
    55. }
    56. else if(len > mR*mR)
    57. {
    58. return 1;
    59. }
    60. else if(len < mR*mR)
    61. {
    62. return -1;
    63. }
    64. }
    65. };
    66. void test()
    67. {
    68. //实例化一个点的对象
    69. Point p;
    70. p.setX(5);
    71. p.setY(5);
    72. //实例化一个圆的对象
    73. Circle cir;
    74. cir.setPoint(2,2);
    75. cir.setR(5);
    76. if(cir.pointIsOnCircle(p) == 0)
    77. {
    78. cout<<"点在圆上"<<endl;
    79. }
    80. else if(cir.pointIsOnCircle(p) > 0)
    81. {
    82. cout<<"点在圆外"<<endl;
    83. }
    84. else if(cir.pointIsOnCircle(p) < 0)
    85. {
    86. cout<<"点在圆内"<<endl;
    87. }
    88. }

    4、类中成员函数在类外实现

    5、类在其他文件实现

            头文件定义类, cpp实现类的成员函数

    data.h

    1. #ifndef DATA_H
    2. #define DATA_H
    3. class Data
    4. {
    5. private:
    6. int mA;
    7. public:
    8. int getA(void);
    9. void setA(int a);
    10. };
    11. #endif

     data.cpp

    1. #include "data.h"
    2. int Data::getA()
    3. {
    4. return mA;
    5. }
    6. void Data::setA(int a)
    7. {
    8. mA = a;
    9. }

    main.cpp

    1. #include <iostream>
    2. #include "data.h"
    3. using namespace std;
    4. int main(int argc, char *argv[])
    5. {
    6. Data ob;
    7. ob.setA(100);
    8. cout<<ob.getA()<<endl;
    9. return 0;
    10. }

    对象的构造和析构:

            当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据。对象的初始化清理也是两个非常重要的安全问题,C++为了给我们提供这种问题的解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用完成对象初始化(创建对象时为对象成员属性赋值)和对象清理工作。初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事。

    四、构造函数(初始化工作)

    1、概述

            构造函数是类实例化对象时自动调用

    2、创建

            构造函数名与类名称相同,不能有返回值类型(连void都不可以),可以有参数(支持重载),必须加public权限。

    3、分类

            无参构造、有参构造。

    4、调用

            类实例化对象时:先为对象开辟空间 然后才调用构造函数。 

    1、如果用户不提供构造函数 编译器会自动 提供一个无参空的构造函数。

    2、如果用户提供构造函数 编译器会自动 屏蔽默认无参的构造

    四种:隐式调用、显示调用、隐式转换、匿名调用 

    注:写任何一个类 无参构造, 有参构造都需要实现 

    5、explicit关键字防止构造函数隐式转换

            explicit修饰构造函数,防止构造函数隐式转换,避免令人产生赋值误会。

    允许隐式转换:

    1. //此时允许有参构造隐式转换
    2. A(int a)
    3. {
    4. mA = a;
    5. cout<<"A的有参构造mA="<<mA<<endl;
    6. }
    1. //构造函数隐式转换(类中只有一个数据成员)
    2. A ob1=100;//ok

    不允许隐式转换:

    1. //防止有参构造 隐式转换
    2. explicit A(int a)
    3. {
    4. mA = a;
    5. cout<<"A的有参构造mA="<<mA<<endl;
    6. }
    1. //构造函数隐式转换(类中只有一个数据成员)
    2. A ob1=100;//err 转换失败

    五、析构函数(清理工作)

             当对象生命周期结束的时候 系统自动调用析构函数。

            函数名和类名称相同,在函数名前加~,没有返回值类型,没有函数形参。(不能被重载) 先调用析构函数 再释放对象的空间。

    调用释放顺序:括号内的最先释放,释放先进后出

             一般情况下,空的析构函数就足够。但是如果一个类有指针成员,这个类必须 写析构函数,释放指针成员所指向空间。

    1. #include<iostream>
    2. #include<stdlib.h>
    3. #include<string.h>
    4. using namespace std;
    5. class Data
    6. {
    7. public:
    8. char *name;
    9. public:
    10. Data()
    11. {
    12. name = NULL;
    13. }
    14. Data(char *str)
    15. {
    16. name = (char*)calloc(1, strlen(str) + 1);
    17. strcpy(name, str);
    18. cout << "有参构造 name=" << name << endl;
    19. }
    20. ~Data()
    21. {
    22. cout << "析构函数" <<name<< endl;
    23. if (name != NULL)
    24. {
    25. free(name);
    26. name = NULL;
    27. }
    28. }
    29. };
    30. void test()
    31. {
    32. Data ob("hello world");
    33. }

    六、拷贝构造函数

    1、定义

            拷贝构造函数本质是构造函数

            拷贝构造的调用时机:对象 初始化 对象 时。

     如果用户不提供拷贝构造 编译器会自动提供一个默认的拷贝构造(完成赋值动作--浅拷贝)

    2、拷贝构造 和 无参构造 有参构造的关系

    如果用户定义了 拷贝构造或者有参构造 都会屏蔽无参构造。

    如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造。

    3、拷贝构造调用形式

    (1)旧对象给新对象初始化 调用拷贝构造

    1. Data ob1(10);
    2. Data ob2 = ob1;//调用拷贝构造

    (2)普通对象作为函数参数 调用函数时 会发生拷贝构造 

    (3)函数返回值普通对象 (Visual Studio会发生拷贝构造) 

    1. Data get(void)
    2. {
    3. Data ob1(10);
    4. return ob1;
    5. }
    6. void test()
    7. {
    8. Data ob2 = get();
    9. }

    注:给对象取别名 不会调用拷贝构造 

    1. Data ob1(10);
    2. Data &ob2 = ob1;//不会调用拷贝构造

    4、拷贝构造中浅拷贝和深拷贝

            如果类中没有指针成员, 不用实现拷贝构造和析构函数。

            如果类中指针成员和拷贝构造调用, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作(因拷贝构造释放时相应堆区空间需再释放一次,故堆区空间也需重新拷贝一次)

     七、初始化列表

    成员对象:一个类的对象 作为另一个类的成员

    类会自动调用成员对象的无参构造,如果类中想调用成员对象有参构造 必须使用初始化列表

    1. class A
    2. {
    3. public:
    4. int mA;
    5. public:
    6. A()
    7. {
    8. mA = 0;
    9. cout << "A的无参构造" << endl;
    10. }
    11. A(int a)
    12. {
    13. mA = a;
    14. cout << "A的有参构造" << endl;
    15. }
    16. ~A()
    17. {
    18. cout << "A的析构函数" << endl;
    19. }
    20. };
    21. class B
    22. {
    23. public:
    24. int mB;
    25. A ob;//成员对象
    26. public:
    27. B()
    28. {
    29. cout << "B类的无参构造" << endl;
    30. }
    31. //初始化列表 成员对象 必须使用对象名+() 重要
    32. B(int a, int b) :ob(a)
    33. {
    34. mB = b;
    35. cout << "B类的有参构造" << endl;
    36. }
    37. ~B()
    38. {
    39. cout << "B的析构函数" << endl;
    40. }
    41. };
    42. int main(int argc, char* argv[])
    43. {
    44. B ob1(10, 20);
    45. cout << "mA =" << ob1.ob.mA << ", mB =" << ob1.mB << endl;
    46. return 0;
    47. }

     八、对象数组

            对象数组:本质是数组 数组的每个元素是对象

  • 相关阅读:
    Spring系列文章:Spring集成Log4j2⽇志框架、整合JUnit
    前端面试基础面试题——6
    Oracle数据库学习,(一)
    图像特征Vol.1:计算机视觉特征度量|第二弹:【统计区域度量】
    Java8的SerializedLambda详解
    okHttp网络请求结果Response返回主线程中
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校教学过程管理系统34085
    什么是代码签名证书?
    编译原理总结
    【面经】中欧基金笔经面经
  • 原文地址:https://blog.csdn.net/m0_75045191/article/details/132009608