C++类——派生类的构造函数与析构函数

类的构造函数

    每个类都分别定义了它的对象被初始化的方式, 类通过一个或几个特殊的成员函数来控制器对象的初始化过程, 这些函数叫做构造函数

    构造函数的任务是初始化对象的数据成员, 无论何时只要类的对象被创建, 就会执行构造函数。

默认构造函数

    默认构造函数是在未提供显式初始值时, 用来创建对象的构造函数。

    注意: 当且仅当没有定义任何构造函数时, 编译器才会提供默认构造函数。 为类定义了构造函数后, 程序员就必须为它提供默认构造函数, 否则上面的声明将出错。

析构函数

    用构造函数创建对象后, 程序负责跟踪该对象, 直到其过期为止。 对象过期时, 程序将自动调用一个特殊的成员函数, 析构函数, 来完成清理工作。

派生类的构造函数

    在定义派生类时, 派生类并没有把基类的构造函数继承下来

    因此, 派生类的构造函数同时需要对继承的基类成员进行初始化

    派生类构造函数的定义:

派生类名(形式参数列表):基类名(基类构造函数实参列表)
{
派生类初始化函数体
}
  • 形式参数列表中应包含基类成员和派生类成员。
  • 基类名(基类构造函数实参列表), 即调用基类的构造函数。
派生类构造函数的调用顺序:
  1. 调用基类的构造函数。
  2. 执行派生类的构造函数。

派生类的析构函数

    派生类并没有继承基类的析构函数, 所以也需要通过派生类的析构函数去调用基类的析构函数。

    派生类中的析构函数, 用来清理派生类中增加的成员。 基类成员依然由基类的析构函数清理。

     在执行派生类的析构函数时, 编译器会自动调用基类的析构函数。

派生类析构函数的调用顺序:
  1. 执行派生类的析构函数
  2. 调用基类的析构函数

组合关系的派生类

    派生类A和类B是组合关系, 即类A中有类B的子对象。

class A
{
B b1_;
}

    这种情况下, 可以在类A的构造函数中显式地初始化类B的子对象。

类名(形式参数列表):子对象名(子对象构造函数实参列表)
{
类初始化函数体
}

    注意: 如果类B有默认构造函数, 或参数全是默认参数的构造函数, 或有无参数的构造函数, 那么类A的构造函数中可以不用显式初始化子对象。 编译器会自动调用B的构造函数进行初始化。

构造函数和析构函数的调用顺序:
  1. 调用基类构造函数
  2. 调用子对象构造函数
  3. 执行派生类的构造函数
  4. 执行派生类的析构函数
  5. 调用子对象的析构函数
  6. 调用基类的析构函数
#include<iostream>

using namespace std;

class Base
{
protected:
    int bnum1_;
public:
    //基类的构造函数和析构函数
    Base()
    {
        bnum1_ = 0;
        cout << "调用基类的默认构造函数" << endl;
    }
    Base(int bnum1)
    {
        bnum1_ = bnum1;
        cout << "调用基类的构造函数" << endl;
    }
    ~Base()
    {
        cout << "调用基类的析构函数" << endl;
    }

    void print()
    {
        cout << "bnum1_: " << bnum1_ << endl;
    }
};

class Derived : public Base
{
private:
    int dnum1_;
public:
    //派生类的构造函数,需要使用:Base()调用基类的构造函数,不写的话默认调用基类的默认构造函数
    //先基类,再派生类
    Derived() :Base()
    {
        dnum1_ = 0;
        cout << "调用派生类的默认构造函数" << endl;
    }
    Derived(int bnum1, int dnum1) : Base(bnum1)
    {
        dnum1_ = dnum1;
        cout << "调用派生类的构造函数" << endl;
    }
    //派生类的析构函数,编译器会自动地调用基类的析构函数
    //先派生类,再基类
    ~Derived()
    {
        cout << "调用派生类的析构函数" << endl;
    }
    void print()
    {
        cout << "bnum1_: " << bnum1_ << "  dnum1_: " << dnum1_ << endl;
    }
};

class B
{
public:
    int b_;
    B()
    {
        b_ = 0;
        cout << "调用B的默认构造函数" << endl;
    }
    B(int b)
    {
        b_ = b;
        cout << "调用B的构造函数" << endl;
    }
    ~B()
    {
        cout << "调用B的析构函数" << endl;
    }
    void print()
    {
        cout << "b_: " << b_ << endl;
    }
};

class A : public Base
{
private:
    int a_;
    //子对象
    B b1;
public:
    //构造函数调用顺序: 先基类,再是子对象,再是派生类
    A(): b1(), Base()
    {
        a_ = 0;
        cout << "调用A的默认构造函数" << endl;
    }
    //使用的是子对象的名称,而不是子对象的类名
    A(int b, int bnum1, int a) : b1(b), Base(bnum1)
    {
        a_ = a;
        cout << "调用A的构造函数" << endl;
    }
    //析构函数调用顺序: 先派生类,再是子对象,再是基类
    ~A()
    {
        cout << "调用A的析构函数" << endl;
    }
    void print()
    {
        cout << "b_: " << b1.b_ << " bnum1_: "<< bnum1_ << " a_: " << a_ <<  endl;
    }
};

int main()
{
    Base base1;
    Base base2(2);
    base1.print();
    base2.print();

    Derived d1;
    Derived d2(3, 8);
    d1.print();
    d2.print();

    B b1;
    A a1;
    b1.print();
    a1.print();

    B b2(3);
    A a2(6, 8, 10);
    b2.print();
    a2.print();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiaokunzhang/article/details/81003825