前言

今天c++实验让我们分析下列程序的输出

代码与运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include   <iostream>
#include <complex>
using namespace std;

class Base
{
public:
Base() { cout << "Base-ctor" << endl; }
~Base() { cout << "Base-dtor" << endl; }
virtual void f(int) { cout << "Base::f(int)" << endl; }
virtual void f(double) { cout << "Base::f(double)" << endl; }
virtual void g(int i = 10) { cout << "Base::g()" << i << endl; }
};

class Derived : public Base
{
public:
Derived() { cout << "Derived-ctor" << endl; }
~Derived() { cout << "Derived-dtor" << endl; }

void f(complex<double>) {
cout << "Derived::f(complex)" << endl;
}

void g(int i = 20) {
cout << "Derived::g()" << i << endl;
}
};

int main()
{
cout << sizeof(Base) << endl;
cout << sizeof(Derived) << endl;
Base b; // Base-ctor
Derived d; // Base-ctor Derived-ctor
Base *pb = new Derived; // Base-ctor Derived-ctor 就是构造一个派生类,先调用基类构造函数再调用派生类构造函数
b.f(1.0); // Base::f(double)
d.f(1.0); // Derived::f(complex)
pb->f(1.0); // Base::f(double) 1.0->double,派生类中没有(double比complex优先匹配),直接调用基类的double
b.g(); // Base::g()10
d.g(); // Derived::g()20
pb->g(); // Derived::g()10
delete pb; // Base-dtor !!!删除只看指针类型进行析构!!!
// Derived-dtor
// Base-dtor
// Base-dtor
return 0;
}

运行结果

分析

内存大小我也不是很清楚是如何计算的,但是后续输出大概搞懂了

  1. 定义基类对象b:调用基类构造函数

  2. 定义派生类对象d:先调用基类构造函数,再调用派生类构造函数

  3. 定义基类指针指向派生类对象:实际上就是创建了一个派生类对象,同样是先调用基类构造函数,再调用派生类构造函数

  4. b.f(1.0):调用基类的f(double)

  5. d.f(1.0):优先匹配派生类对象的函数,符合complex类型(复数类,如果只输入一个值,默认作为实部),调用派生类的f(complex)

  6. pb->f(1.0):pb为基类指针,f为虚函数但是1.0优先匹配double->调用基类的f(double),如果删除基类的f(double)就会调用派生类的f(complex)

  7. b.g():调用基类的g(int),未指定值默认输出10

  8. d.g():调用派生类的g(int),未指定值默认输出20

  9. pb->g():这个我只是勉强可以理解,但是具体原理我也不太明白,pb为基类指针,g()为虚函数,如果派生类的该同名函数与基类的该函数在参数(个数与数据类型)上完全一致的话,程序会选择执行派生类的该函数并将基类的该函数的形参值赋给派生类的相应形参;如果派生类的该同名函数与基类的该函数在参数(个数与数据类型)上并非完全一致,则直接执行基类的该函数

  10. delete pb:这个很容易判断出错,注意看这个程序对基类析构函数的定义:不是虚析构函数!!!因此虽然定义了一个派生类指针但是却只会执行基类析构函数

    如果是使用系统默认析构函数的话,会一并析构基类与派生类,因为系统定义的析构函数是虚析构函数,因此可以执行到派生类的析构函数,所以我们如果自己要定义析构函数,推荐都定义为虚析构函数,即virtual ~Base() {}