• 【C++】构造函数与类的组合以及初始化


    目录

    目录

    一、构造函数

    1.构造函数出现原因

    2.定义

    3.使用

    4.构造函数调用顺序

    ​​​5.构造函数的作用

    二、类的组合 

    1.引出概念

    三、类成员初始化的困惑——冒号语法

    1.使用说明

    2.注意事项

    3.步骤


    前言:

            每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其他的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C++编译器会自动为A产生四个缺省函数:

    1. A(void);//缺省的构造函数
    2. A(const A &a);//缺省的拷贝构造函数
    3. ~A(void); //缺省的析构函数
    4. A & operate=(const A &a);//缺省的赋值函数

     本文讲述构造函数的相关知识点,其余三个函数会在后面文章陆续发出。

    一、构造函数

    1.构造函数出现原因

    • 举例桌子类Table,属性为长宽高,一个输出函数printf,一个设置函数set,在主函数内定义桌子类的对象t,输出该对象的属性,又定义了桌子属性类的对象t1,设置高为60,输出该对象的属性,如下代码所示:
        1. #include
        2. using namespace std;
        3. class Table
        4. {
        5. public:
        6. void Print();
        7. void Set();
        8. private:
        9. int m_length;//长
        10. int m_width;//宽
        11. int m_height;//高
        12. };
        13. void Table::Print()
        14. {
        15. cout << m_length << " " << m_width << " " << m_height << endl;
        16. }
        17. void Table::Set()
        18. {
        19. int m_length = 120;//长
        20. int m_width = 40;//宽
        21. int m_height = 80;
        22. }
        23. int main()
        24. {
        25. Table t;//定义对象,自动调用Table的构造函数
        26. t.Print();//期待输出结果为120 40 80
        27. }
    • 运行上述代码,输出第一个对象t的属性,期待输出合法的值:120 40 80。但结果出现下面所示的随机值:
    • 解决方案:
      • 1.设置set函数
        1. void main()
        2. {
        3. Table t;
        4. t.Set();
        5. t.Printf();
        6. }
      • 输出结果:
      • 此时是合法值,但是不合理,因为用桌子抽象类定义了对象t,t本身应该就拥有桌子类找个属性,本就应该调用就输出结果,而不是需要再输出设置值
      • 2、定义构造函数(在👇“使用”处,说明如何使用)
    • 通过上面举例发现,必须用Table类的一个成员函数,才能输出Table定义对象的合法值,但成员函数不能在类出现后再调用,而且正常流程应该是在定义完t之后,再给当前属性分配空间赋予它合法值。此时发现,开始自相矛盾了,因此需要一个在定义对象时,系统自动调用类成员函数,给当前对象的属性开辟空间给他合法值的函数——构造函数
    • 构造函数满足的就是:不通过对象去调用初始化对象的数据,当这个对象创建出来的时候,他就已经是具有一定的初始值。
    • 调用构造函数的目的是给对象的属性分配空间,然后才能初始化输出合法值。

    2.定义

    • 构造函数是个特殊的成员函数函数,函数名和类名相同,无返回类型,可以带参数(可以重载)
    • 在使用该类定义的对象时,发现如果程序员没有定义构造函数,则类会提供一个默认的构造函数,给类中的数据成员分配空间如果写了,系统就不会再提供构造函数。上例所示运行结果出现了地址,开辟了空间就调用了构造函数,但是程序员没写,就说明了系统提供默认构造函数。

    3.使用

    • 举例Table桌子类,与上面不同的是,设置构造函数及Table的函数名后带上了参数默认值(如果要带默认值,只能在参数后面初始化的时候写)。示例如下所示:
      1. class Table
      2. {
      3. public:
      4. Table(int l = 120, int w = 40, int h = 80)//带默认值参数的构造函数
      5. {
      6. m_length = l;//长
      7. m_width = w;//宽
      8. m_height = h;//高
      9. }
      10. void Print()
      11. {
      12. cout << m_length << " " << m_width << " " << m_height << endl;
      13. }
      14. private:
      15. int m_length;//长
      16. int m_width;//宽
      17. int m_height;//高
      18. };
      19. int main()
      20. {
      21. Table t;//定义对象,自动调用Table的构造函数
      22. t.Print();
      23. //输出结果为120 40 80
      24. Table t1(60);
      25. t1.Print();
      26. //输出结果为60 40 80
      27. }
    • 此时运行结果就是合法值
    • 说明:
      • 上述main函数内定义了对象t,系统执行时自动调用Table的构造函数Table(int l=120,int w=40,int h=80),输出默认值 120 40 80
      • 如果构造函数内无参Table(int l,int w,int h),那么在调用时会报错,因为程序员自己定义了构造函数,那么系统不会再提供默认值,而程序员自己写的构造函数又是无参的没有默认值,就没办法输出。
    • Table tt[5];//定义对象数组
    • 定义对象数组,说明当前数组内有5个对象,调用了5次构造函数
    • Table *p;//指针变量
    •        定义指针变量,并未定义对象,不会调用构造函数,而且p为指针,在栈内开辟了四个字节,不需要构造函数再为其开辟内存。
    • 使用:
      1. Table *p=&t;//指向t
      2. p->print();
    • 在定义一个属性的对象时,系统会检测是否有程序员自己写的构造函数,有的话就直接调用程序员写的。如果没有的话,系统就会调用自己的构造函数。

    4.构造函数调用顺序

    • 遇到对象->自动调用当前类的构造函数,调用步骤:
      • 1.传参
      • 2.根据数据成员在类中的声明顺序开辟空间
      • 3.执行构造函数的函数体

    ​​​5.构造函数的作用

    构造函数三作用:

    • 创造对象
    • 初始化对象
    • 隐式类型转换

    注意没有开辟空间。

    对象不可以调用构造函数,但是可以调用析构

    析构函数没有重载,构造函数有


    二、类的组合 

    1.引出概念

    • 对电脑类键盘鼠标屏幕的属性进行举例:
    • 首先,键盘、鼠标....这些不是Int什么类型,他们都有自己的属性,因此他们自身就需要一个类来描述。
    • 列个框架,对其举例说明:
        1. class CPU
        2. {
        3. public:
        4. CPU()//CPU的构造属性
        5. {
        6. cout<<"CPU"<
        7. }
        8. };
        9. class Mouse
        10. {
        11. public:
        12. Mouse()//鼠标的构造属性
        13. {
        14. cout<<"Mouse"<
        15. }
        16. };
        17. class KeyBoard
        18. {
        19. public:
        20. KeyBoard()//屏幕的构造属性
        21. {
        22. cout<<"KeyBoard"<
        23. }
        24. };
        25. class Computer
        26. {
        27. public:
        28. Computer()//电脑的构造属性
        29. {
        30. cout<<"Conputer"<
        31. }
        32. private:
        33. CPU cpu;
        34. Mouse ms;
        35. KeyBoard kb;
        36. };
        37. int main()
        38. {
        39. Computer c;
        40. }
        • 运行结果:
    • 一个类的对象(cpu,mouse,keyboard)作为另一个类(computer)的数据成员出现,叫做类的组合
    • 另外一个包含的关系是聚合
    • 组合是强拥有的关系——整体和部分(cpu坏了conputer也会坏),聚合是弱拥有的关系(就像公司与员工的关系一样,员工离职公司也会照样存在)

    三、类成员初始化的困惑——冒号语法

    • 以下面代码学生和日期类进行举例:
      1. class Date//日期类
      2. {
      3. public:
      4. Date(int y,int m,int d)
      5. {
      6. m_y=y;//年
      7. m_m=m;//月
      8. m_d=d;//日
      9. }
      10. void Show()
      11. {
      12. cout<" "<" "
      13. }
      14. private:
      15. int m_y;
      16. int m_m;
      17. int m_d;
      18. };
      19. class Student
      20. {
      21. public:
      22. Student(int num,char *num,Date d)
      23. {
      24. m_num=num;//编号
      25. strcpy(m_name,name);//字符串赋值
      26. birthday=d//生日
      27. }
      28. void Print()
      29. {
      30. cout<" "<" ";
      31. //输出birthday:
      32. //cout<
      33. //cout<
      34. birthday.Show();
      35. ]
      36. private:
      37. int m_num;
      38. char m_name[20];
      39. Date birthday;
      40. };
      41. void main()
      42. {
      43. Date d(2002,12,23);//定义对象,传参
      44. Student s(1001,"lisi",d);
      45. s.printf();
      46. }
    • 代码解释:

      • 定义了日期类包含年year月month日day的属性以及一个输出函数

      • 定义了学生类包含编号num生日birthday的属性以及一个输出函数

      • 在主函数内定义Date类的d对象并初始化

      • 主函数内定义Student类的s对象并初始化

      • 输出对象s的属性信息

    • 问题:
      • 编号1001和姓名lisi都可以按照流程传参输出,但是birthday的d传到Date birthday处后,birthday作为Date的对象,理应调用Date的构造函数来输出初始年月日信息,但是在Date未进行参数初始化,无法进行赋初值输出(上面说了,因为程序员写了构造函数后不论有没有初始化参数,系统都不会再调用自己的构造函数提供默认值)
    • 解决方案:
      • 1.在参数处进行初始化,并在内部输出结果
        • 运行结果:
        • 这也可以输出我们想要的结果,但是问题又出现了,为什么输出了两个值,第一行输出年月日初始化结果,第二行输出对象s的属性信息。怎么能在不要第一行的情况下还能有第二行的信息输出?下面就引入了冒泡语法👇
    • 2,使用冒泡语法解决
      • 冒泡语法的使用:在构造函数的参数后面写一个冒号,然后进行初始化
      • 输出结果:

    1.使用说明

    • birthday(d)的意思是把d的值赋给了birthday
    • :后面的内容是初始化,大括号{}里的内容是赋初值,赋初值≠初始化
    • 用冒泡语法赋值的,就不必再在构造函数内进行初始化。

    2.注意事项

    • 是一个冒号:,两个冒号::是作用域。
    • :后面只能用(),不能用赋值号=
    • 在构造函数体内必须对参数赋初值(赋初值≠初始化)

    3.步骤

    • 调用构造函数三步骤
      • 传参
      • 根据数据成员在类里面的声明顺序,用冒号语法后面的值进行初始化
      • 执行构造函数函数体
    • 一个例题
      • 输出结果为 4 8吗?
      • 并不是 实际结果为 一个随机值 和8
      • 解释:
        • 构造函数一步——传参:4->i; 8->j
        • 第二步——数据成员在类里面的声明顺序,用冒号语法后面的值进行初始化:先声明的m_i,然后对m_i初始化:m_i(m_j),此时的m_j是随机值,因此m_i的输出结果也为随机值,然后对m_j(j) ->m_j=8
        • 第三步执行构造函数体,输出结果。

  • 相关阅读:
    关于linux的ssh(出现的问题以及ubuntu的ssh配置)
    HM4063原厂5A三节锂电池充电管理集成电路
    【SQL中limit的用法】
    C++对象移动
    Vue3 - 事件 API 新标准(如何在 Vue3 中怎么用事件总线实现兄弟组件通信?相比 Vue2 有什么不同?)
    MySQL数据库的查询操作
    Vue数据代理,事件处理,计算属性
    矩阵的模和内积
    ARouter出现 there‘s no route matched in group问题排查
    [附源码]JAVA毕业设计公务用车管理智慧云服务监管平台(系统+LW)
  • 原文地址:https://blog.csdn.net/qq_53830608/article/details/127095212