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 构造函数的调用顺序
对象创建时,构造函数的调用顺序如下:
- 调用父类的构造函数
- 调用成员变量的构造函数
- 调用类自身的构造函数
上面的调用顺序可以总结出一句口诀:先父母,后客人,再自己
编程实验:构造函数的调用顺序
// 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 析构函数的调用顺序
析构函数的调用顺序与构造函数相反
- 执行自身的析构函数
- 执行成员变量的析构函数
- 执行父类的析构函数
编程实验:析构函数调用顺序
// 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、析构函数与构造函数调用顺序相反