-- TOC --
一直有些困惑,C++继承的时候,构造函数和析构函数是按什么顺序调用的,子类如何调用父类的接口。为此,我专门写了一段测试代码,完美的搞清楚了这些问题。
请看代码:
#include <cstdio>
struct aa {
aa() {
printf("aa constructor\n");
}
void init() {
a = 123;
}
~aa() {
printf("aa destructor\n");
}
unsigned int a;
};
struct bb: aa {
bb() {
printf("bb constructor\n");
}
void init() {
aa::init();
b = 234;
}
~bb() {
printf("bb destructor\n");
}
unsigned int b;
};
struct cc: bb {
cc() {
printf("cc constructor\n");
}
void init() {
bb::init();
c = 345;
}
~cc() {
printf("cc destructor\n");
}
unsigned int c;
};
int main(void){
cc c{};
c.init();
printf("%u %u %u\n", c.a, c.b, c.c);
return 0;
}
代码中定义了3个类,单线继承关系,每个类包含一个自己的成员变量,每次继承,都增加一个成员变量,并增加一个init接口用来给这个变量赋值。在init接口中,调用父类的init接口,去初始化在父类中定义的成员变量。
以上代码的运行效果:
$ g++ -Wall -Wextra test.cpp -o test
$ ./test
aa constructor
bb constructor
cc constructor
123 234 345
cc destructor
bb destructor
aa destructor
各位同学可以清晰的看到,构造函数的调用,从最开始的父类开始,一层层往下。而析构函数的调用顺序刚好相反。init接口成功调用父类接口,没有异常。
cc::init也可以直接调用aa::init,这样会导致b没有初始化,打印出来是个随机值。
但,上面的测试代码,让我产生了一个新的困惑,子类一定会调用父类的构造和析构接口吗?
首先,如果父类是POD,即C语言的struct,只有数据,没有成员函数。继承这样的父类,只是继承了一推数据,父类没有任何接口可供调用。比如下面的代码:
#include <cstdio>
class aa {
public:
unsigned int va;
};
class bb: public aa {
public:
bb(unsigned int a) {
va = a;
}
};
int main(void) {
bb b(12);
printf("%u\n", b.va);
return 0;
}
使用class关键词,要多写一些代码,而且默认是private,我更喜欢默认是public的struct!
如果父类存在函数接口,此时子类就肯定会调用父类的构造或析构接口,显式地或隐式地。
下面是显式地调用示例:
#include <cstdio>
class aa {
public:
unsigned int va;
aa(unsigned int a):
va{a} {
}
};
class bb: public aa {
public:
bb(unsigned int a):
aa{a} {
// aa(a); // we don't need create an aa object
}
};
int main(void) {
bb b(12);
printf("%u\n", b.va);
return 0;
}
基类aa定了一个数据va,也定义了初始化va的代码。子类bb在实例化的时候,在初始化列表中,初始化aa中定义的数据。
下面是隐式地调用示例:
#include <cstdio>
class aa {
public:
unsigned int va;
aa() {
printf("aa constructor\n");
}
aa(unsigned int a):
va{a} {
}
};
class bb: public aa {
public:
bb(unsigned int a) {
va = a;
}
};
int main(void) {
bb b(12);
printf("%u\n", b.va);
return 0;
}
运行这段代码,会看到:
$ g++ -Wall -Wextra test.cpp -o test
$ ./test
aa constructor
12
基类aa的无参数的constructor,被隐式地调用了。
以上示例,都是省掉了destructor,因为都是简单数据类型,destructor编译自动提供了。
本文链接:https://cs.pynote.net/sf/c/cpp/202209062/
-- EOF --
-- MORE --