一直以来,重写、重载、隐藏一直都是C++中被经常讨论的基础知识点。

恰好最近遇到一个相关的问题,被人问到子类初始化的时候new出父类然后动态转换成子类,然后调用父类对象中的二者同名函数,会执行哪个。之前网上博客和讨论一般的集中在子类调用同名方法而非父,所以一时不得知晓准确情形。大概猜想转换后应该是调用父类的方法。

后被告知new出的是子类,执行的当然是子类的方法。

本着有问题写出来跑跑就知道了的想法,有空便大概写了一下跑跑这种情况。顺便又复习了一下三种技术的特征和知识点,这篇文章写的比较简明扼要,可以快速弄清三者知识区分。

首先是讨论的那种情形:

隐藏:是指派生类的函数屏蔽了与其同名的基类函数,注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。

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
class A
{
public:
A() {}

void print1()
{
cout << "A print!" << endl;
}
};

class B : public A
{
public:
B() {}

void print1()
{
cout << "B print!" << endl;
}
};

int main()
{
A *a1 = dynamic_cast<A*>(new B());
a1->print1();
dynamic_cast<B*>(a1)->print1();

A *a2 = (A*)(new B());
a2->print1();
dynamic_cast<B*>(a2)->print1();

return 0;
}

运行结果为:

1
2
3
4
A print!
B print!
A print!
B print!

看来转换后的父对象并没有如上所说的执行子方法,不论是动态转换还是强转结果父对象都还是调用了父方法。

于是试了另一种重写(覆盖)的情况:

重写(覆盖):是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。

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
49
50
51
52
53

class A
{
public:
A() {}

virtual ~A() {}

void print1()
{
cout << "A print!" << endl;
}

virtual void print2()
{
cout << "virtual A print!" << endl;
}
};

class B : public A
{
public:
B() {}

virtual ~B() {}

void print1()
{
cout << "B print!" << endl;
}

virtual void print2()
{
cout << "virtual B print!" << endl;
}
};

int main()
{
A *a1 = dynamic_cast<A*>(new B());
a1->print1();
dynamic_cast<B*>(a1)->print1();
a1->print2();
dynamic_cast<B*>(a1)->print2();

A *a2 = (A*)(new B());
a2->print1();
dynamic_cast<B*>(a2)->print1();
a2->print2();
dynamic_cast<B*>(a2)->print2();

return 0;
}

运行结果为:

1
2
3
4
5
6
7
8
A print!
B print!
virtual B print!
virtual B print!
A print!
B print!
virtual B print!
virtual B print!

可见,对于重写方法,直接对象调用不论怎么转换,调用的都是子类方法。

这也给自己启示,技术上的细节还是要多多学习,多多实践,毕竟实践出真知。