首页 > 编程笔记

C++派生类的构造函数和析构函数(入门必读)

在 C++ 中,继承使得派生类的内部包含了基类的成员,因此派生类也必须负责基类成员的构造和析构。

派生类的构造函数

首先,构造函数是不能被继承的,派生类必须重新定义自己的构造函数。

在派生类的构造函数中,至少要完成以下两件事情:
定义派生类的构造函数时,应当在初始化列表中依次写上基类的构造函数和成员的初始化参数。特别是在基类和数据成员的构造函数带有参数的情况下,其参数只能由开发者指定,所以必须显式地初始化。其语法如下:
派生类构造函数名(参数列表):基类构造函数名(参数列表), 派生类成员初始化
{
    ...... //派生类成员初始化的过程也可以写在函数内部
}
例如,基类的构造函数是:
Base(int b);
派生类新增的成员变量是:
int m_a;
那么派生类的构造函数应该定义为:
Derived(int b, int a):Base(b), m_a(a){
    //...... 也可以将 m_a=a; 写在函数内部
}
对于有默认构造函数的基类,定义派生类的构造函数时可以不显式地调用基类的构造函数。如果派生类新增成员变量的类型是内置的数据类型,或者是具有默认构造函数的类,那么编译器也会自动添加成员变量的初始化代码。

在派生类构造函数的初始化列表中,编译器先调用基类的构造函数,然后是各个成员的初始化,各个成员的初始化是按照它们定义的顺序进行的。

举个简单的例子:
#include <iostream>

class Base {
public:
    Base() {
        std::cout << "Base constructor" << std::endl;
    }
};

class MemberA {
public:
    MemberA() {
        std::cout << "MemberA constructor" << std::endl;
    }
};

class MemberB {
public:
    MemberB() {
        std::cout << "MemberB constructor" << std::endl;
    }
};

class Derived : public Base {
private:
    MemberA memberA;
    MemberB memberB;

public:
    // 派生类构造函数
    Derived() : memberB(), memberA() {
        std::cout << "Derived constructor" << std::endl;
    }
};

int main() {
    Derived d;
    return 0;
}
示例中有一个基类 Base 和两个成员类 MemberA 和 MemberB,然后有一个派生类 Derived,其中包含了这两个成员。在 Derived 的构造函数里,可以看到初始化列表中首先是 memberA,然后是 memberB。

运行这段代码,输出结果为:

Base constructor
MemberA constructor
MemberB constructor
Derived constructor

从结果不难看出,编译器确实是先调用基类构造函数,然后按照它们在派生类中的定义顺序去初始化各个成员。

派生类的析构函数

和构造函数一样,析构函数也不能被继承,所以派生类也需要有自己的析构函数。

与构造函数不同的是,由于析构函数没有参数,所以基类和新增数据成员的析构可以由编译器代劳,即在编译时由编译器插入相关的析构代码。

派生类的析构与构造顺序刚好相反,其顺序是先调用派生类的析构函数,然后析构派生类新增的成员,最后析构基类对象。

修改上面的程序,为各个类手动添加析构函数:
#include <iostream>

class Base {
public:
    Base() {
        std::cout << "Base constructor" << std::endl;
    }

    ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class MemberA {
public:
    MemberA() {
        std::cout << "MemberA constructor" << std::endl;
    }

    ~MemberA() {
        std::cout << "MemberA destructor" << std::endl;
    }
};

class MemberB {
public:
    MemberB() {
        std::cout << "MemberB constructor" << std::endl;
    }

    ~MemberB() {
        std::cout << "MemberB destructor" << std::endl;
    }
};

class Derived : public Base {
private:
    MemberA memberA;
    MemberB memberB;

public:
    Derived() : memberB(), memberA() {
        std::cout << "Derived constructor" << std::endl;
    }

    ~Derived() {
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Derived d;
    return 0;
}
运行结果为:

Base constructor
MemberA constructor
MemberB constructor
Derived constructor
Derived destructor
MemberB destructor
MemberA destructor
Base destructor

从输出结果不难看出,析构过程确实是按照预期的顺序进行的:先析构派生类的成员(memberB、memberA),然后析构派生类自身,最后析构基类对象。

推荐阅读