【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,输出该对象的属性,如下代码所示:
    • #include
      using namespace std;
      
      class Table
      {
      public:
          void Print();
          void Set();
      private:
          int m_length;//长
          int m_width;//宽
          int m_height;//高
      
      
      };
      void Table::Print()
      {
          cout << m_length << " " << m_width << " " << m_height << endl;
      }
      void Table::Set()
      {
          int m_length = 120;//长
          int m_width = 40;//宽
          int m_height = 80;
      }
      
      int main()
      {
          Table t;//定义对象,自动调用Table的构造函数
          t.Print();//期待输出结果为120 40 80
      }
  • 运行上述代码,输出第一个对象t的属性,期待输出合法的值:120 40 80。但结果出现下面所示的随机值:
  • 【C++】构造函数与类的组合以及初始化_第1张图片
  • 解决方案:
    • 1.设置set函数
    • void main()
      {
          Table t;
          t.Set();
          t.Printf();
      }
    • 输出结果:
      • 【C++】构造函数与类的组合以及初始化_第2张图片
    • 此时是合法值,但是不合理,因为用桌子抽象类定义了对象t,t本身应该就拥有桌子类找个属性,本就应该调用就输出结果,而不是需要再输出设置值
    • 2、定义构造函数(在“使用”处,说明如何使用)
  • 通过上面举例发现,必须用Table类的一个成员函数,才能输出Table定义对象的合法值,但成员函数不能在类出现后再调用,而且正常流程应该是在定义完t之后,再给当前属性分配空间赋予它合法值。此时发现,开始自相矛盾了,因此需要一个在定义对象时,系统自动调用类成员函数,给当前对象的属性开辟空间给他合法值的函数——构造函数
  • 构造函数满足的就是:不通过对象去调用初始化对象的数据,当这个对象创建出来的时候,他就已经是具有一定的初始值。
  • 调用构造函数的目的是给对象的属性分配空间,然后才能初始化输出合法值。

2.定义

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

3.使用

  • 举例Table桌子类,与上面不同的是,设置构造函数及Table的函数名后带上了参数默认值(如果要带默认值,只能在参数后面初始化的时候写)。示例如下所示:
  • class Table
    {
    public:
        Table(int l = 120, int w = 40, int h = 80)//带默认值参数的构造函数
        {
            m_length = l;//长
            m_width = w;//宽
            m_height = h;//高
        }
        void Print()
        {
            cout << m_length << " " << m_width << " " << m_height << endl;
        }
    private:
        int m_length;//长
        int m_width;//宽
        int m_height;//高
    };
    
    int main()
    {
        Table t;//定义对象,自动调用Table的构造函数
        t.Print();
        //输出结果为120 40 80
        Table t1(60);
        t1.Print();
        //输出结果为60 40 80
    }
  • 此时运行结果就是合法值
  • 说明:
    • 上述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为指针,在栈内开辟了四个字节,不需要构造函数再为其开辟内存。
  • 使用:
  • Table *p=&t;//指向t
    p->print();
  • 在定义一个属性的对象时,系统会检测是否有程序员自己写的构造函数,有的话就直接调用程序员写的。如果没有的话,系统就会调用自己的构造函数。

4.构造函数调用顺序

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

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

构造函数三作用:

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

注意没有开辟空间。

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

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


二、类的组合 

1.引出概念

  • 对电脑类键盘鼠标屏幕的属性进行举例:
  • 首先,键盘、鼠标....这些不是Int什么类型,他们都有自己的属性,因此他们自身就需要一个类来描述。
  • 列个框架,对其举例说明:
    • class CPU
      {
      public:
          CPU()//CPU的构造属性
          {
              cout<<"CPU"<
      • 运行结果:
      • 【C++】构造函数与类的组合以及初始化_第3张图片
  • 一个类的对象(cpu,mouse,keyboard)作为另一个类(computer)的数据成员出现,叫做类的组合
  • 另外一个包含的关系是聚合
  • 组合是强拥有的关系——整体和部分(cpu坏了conputer也会坏),聚合是弱拥有的关系(就像公司与员工的关系一样,员工离职公司也会照样存在)

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

  • 以下面代码学生和日期类进行举例:
  • class Date//日期类
    {
    public:
        Date(int y,int m,int d)
        {
            m_y=y;//年
            m_m=m;//月
            m_d=d;//日
        }
        void Show()
        {
            cout<
  • 代码解释:

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

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

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

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

    • 输出对象s的属性信息

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

1.使用说明

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

2.注意事项

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

3.步骤

  • 调用构造函数三步骤
    • 传参
    • 根据数据成员在类里面的声明顺序,用冒号语法后面的值进行初始化
    • 执行构造函数函数体
  • 一个例题
    • 【C++】构造函数与类的组合以及初始化_第8张图片
    • 输出结果为 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
      • 第三步执行构造函数体,输出结果。

你可能感兴趣的:(C++,c++)