C++虚函数的意义

下面以一个简单的例子说明virtual的作用,作为继承和多态的重要组件,如果没有虚函数,那么多态的实现将非常的困难。首先明确一点,类成员函数中this的类型取决于该函数定义的位置,在父类中,this的类型是父类,在子类中,this的类型是子类。比如父类A和B,父类有一个成员函数test(), 其函数体中通过this调用了test1, 如果子类没有override这个函数,那么子类中,test()函数体里面this的类型依然是父类的类型。我们看下面三个例子来理解这一点:

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
#include <iostream>
#include <thread>
//This function will be called from a thread
class A{
public:
void test(){
this->test1();
}
void test1(){
std::cout << "I am base" << std::endl;
}
};

class B:public A
{
virtual void test1(){
std::cout << "I am Not Base" << std::endl;
}
};

int main() {
//Launch a thread
B b;
b.test();
return 0;
}
// 运行结果
// I am base
// I am base

这个例子中,子类B没有覆盖A::test,因此test函数体内,this类型依然是A*,这就造成了b.test()调用的是A::test1(),这往往不是想要的结果。而如果加上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
#include <iostream>
class A{
public:
void test(){
this->test1();
}
virtual void test1(){
std::cout << "I am base" << std::endl;
}
};

class B:public A
{
virtual void test1(){
std::cout << "I am Not Base" << std::endl;
}
};

int main() {
B b;
b.test();

A a;
a.test();
return 0;
}
// 运行结果
// I am Not Base
// I am base

这个例子中,test1是虚函数,因此即使B没有覆盖test函数,this的编译时类型依然是A*,但是在调用this->test1()的时候会查找虚函数表,然后知道this实际类型应该是B*并调用B::test1()。具体要调用A::test1还是B::test1只能在运行时知道。注意哦,如果子类同时覆盖了调用的函数test,那么也是能够调用到想要的test1的,因为此时编译时this的类型就已经是B*了。看下面的代码:

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
#include <iostream>
class A{
public:
void test(){
this->test1();
}
void test1(){
std::cout << "I am base" << std::endl;
}
};

class B:public A
{
public:
void test1(){
std::cout << "I am Not Base" << std::endl;
}
void test(){
this->test1();
}
};

int main() {
B b;
b.test();

A a;
a.test();
return 0;
}
// 运行结果
// I am Not Base
// I am base

0%