【C++深度解析】32、继承中的构造与析构

1 父类构造函数在子类中的调用方式

子类不能继承父类的构造函数,所以在子类中需要调用父类的构造函数来完成初始化。调用方式有两种

  • 隐式调用:适用于无参构造函数使用默认参数的构造函数
  • 显式调用:通过初始化列表调用,适用于所有父类构造函数

构造规则如下:

  • 先调用父类构造函数在执行子类的构造函数,可以隐式调用或显示调用

编程实验:子类调用父类构造函数

// 32-1.cpp
#include<iostream>
using namespace std;
class Parent
{
public:
    Parent()
    {
        cout << "Parent()" << endl;
    }
    Parent(string s)
    {
        cout << "Parent(string s) : " << s << endl;
    }
};
class Child : public Parent
{
public:
    Child()
    {
        cout << "Child()" << endl;
    }
    Child(string s) : Parent(s)
    {
        cout << "Child(string s) : " << s << endl;
    }
};
int main()
{
    Child c;
    Child cc("cc");
    return 0;
}
  • 第 19 行没有指定父类的构造函数,默认调用父类无参构造函数 Parent()。
  • 第 23 行,构造函数 Child(string s) 通过初始化列表显式调用父类构造函数 Parent(s)

2 构造函数的调用顺序

对象创建时,构造函数的调用顺序如下:

  1. 调用父类的构造函数
  2. 调用成员变量的构造函数
  3. 调用类自身的构造函数

上面的调用顺序可以总结出一句口诀:先父母,后客人,再自己

编程实验:构造函数的调用顺序

// 32-2.cpp
#include<iostream>
using namespace std;
class Object
{
public:
    Object(string s)
    {
        cout << "Object(string s) : " << s << endl;
    }
};
class Parent : public Object
{
public:
    Parent() : Object("Default")
    {
        cout << "Parent()" << endl;
    }
    Parent(string s) : Object(s)
    {
        cout << "Parent(string s) : " << s << endl;
    }
};
class Child : public Parent
{
public:
    Child() : mO1("Default 1"), mO2("Default 2")
    {
        cout << "Child()" << endl;
    }
    Child(string s) : Parent(s), mO1(s+"1"), mO2(s + "2")
    {
        cout << "Child(string s) : " << s << endl;
    }
private:
    Object mO1;
    Object mO2;
};
int main()
{
    Child c("c");
    return 0;
}
  • Object 是 Parent 的父类,Parent 是 Child 的父类,同时 Object 是 Child 的成员变量;
  • 由于 Object 没有无参构造函数或有默认参数的构造函数(我们已经定义了构造函数,编译器不会生成默认构造函数了),所以 Child 类中两个构造函数都要显示的调用 Object 的构造函数 Object(string s);
  • 第 27 行隐私调用了父类的无参构造函数 Parent(),第 31 行显式调用了父类的构造函数 Parent(string s);
  • Child 类中的构造函数需要通过列表初始化完成 Object 成员的构造,完成 mO1 和 mO2 的初始化;

按照先父母,后客人,再自己的原则
要完成对象 Child c(“c”) 的构造,首先调用父类的构造函数 Parent(string s) ,Object 又是 Parent 的父类,所以先调用 Object(string s),在执行 Parent(string s) 的内容。再调用成员变量的构造函数 Object(string s) 完成成员 mO1 和 mO2 的初始化,初始化顺序与成员变量声明顺序相同。最后调用自己的构造函数。

编译运行:

$ g++ 32-2.cpp -o 32-2
$ ./32-2
Object(string s) : c
Parent(string s) : c
Object(string s) : c1
Object(string s) : c2
Child(string s) : c

3 析构函数的调用顺序

析构函数的调用顺序与构造函数相反

  1. 执行自身的析构函数
  2. 执行成员变量的析构函数
  3. 执行父类的析构函数

编程实验:析构函数调用顺序

// 32-3.cpp
#include<iostream>
using namespace std;
class Object
{
public:
    Object(string s)
    {
        cout << "Object(string s) : " << s << endl;
        ms = s;
    }
    ~Object()
    {
        cout << "~Object() : " << ms << endl;
    }
private:
    string ms;
};
class Parent : public Object
{
public:
    Parent() : Object("Default")
    {
        cout << "Parent()" << endl;
        ms = "Default";
    }
    Parent(string s) : Object(s)
    {
        cout << "Parent(string s) : " << s << endl;
        ms = s;
    }
    ~Parent()
    {
        cout << "~Parent() : " << ms << endl;
    }
private:
    string ms;
};
class Child : public Parent
{
public:
    Child() : mO1("Defalut 1"), mO2("Default 2")
    {
        cout << "Child()" << endl;
        ms = "Default";
    }
    Child(string s) : Parent(s), mO1(s + "1"), mO2(s + "2")
    {
        cout << "Child(string s)" << s << endl;
        ms = s;
    }
    ~Child()
    {
        cout << "~Child()" << ms << endl;
    }
private:
    Object mO1;
    Object mO2;
    string ms;
};
int main()
{
    Child("c");
    return 0;
}

和 32-2.cpp 代码类似,编译运行:

$ g++ 32-3.cpp -o 32-3
$ ./32-3
Object(string s) : c
Parent(string s) : c
Object(string s) : c1
Object(string s) : c2
Child(string s)c
~Child()c
~Object() : c2
~Object() : c1
~Parent() : c
~Object() : c

4 小结

1、子类对象在创建时需要调用父类构造函数进行初始化
2、使用初始化列表调用父类构造函数,有隐式调用和显式调用
3、构造函数的调用顺序:先父母,后客人,再自己
4、析构函数与构造函数调用顺序相反

发布了298 篇原创文章 · 获赞 181 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/happyjacob/article/details/104321386
今日推荐