前言

关键字

  • 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
class MoveObject{
public:
int x;
int y;
void Move(){
x++;
y++;
}
};

class NPCObject :public MoveObject{
public:
void Move(){
x++;
y++;
std::cout << "NPCobject!\n";
}
};

class MonsterObject :public MoveObject{
public:
void Move(){
x++;
y++;
std::cout << "monsterobject!\n";
}
};

在多态之前控制这种行为的时候,一般都是通过传入参数不同

1
2
3
4
5
6
7
void move(NPCObject *obj){
obj->Move();
}

void move(MonsterObject *obj){
obj->Move();
}

学习了多态之后,因为有向下转型的特性:

1
2
3
void move(MoveObject *obj){
obj->Move();
}

我们就可以通过这种方式传递。

这里没有输出是因为派生类调用的是基类的move,基类的move里面我们没有输出项。


当我们设置基类的move函数为虚函数时,其实就是告诉编译器基类的这个函数是虚的,派生类可以自定义。

1
2
3
4
5
6
7
8
9
class MoveObject{
public:
int x;
int y;
void virtual Move(){ //虚函数
x++;
y++;
}
};

看到基类的函数设置成虚函数之后,在派生类调用的时候就会自动匹配到派生类的函数。

本质上就是动态绑定,因为基类的函数并不是说从内存上被覆盖了,而是派生类有重写的情况下就用派生类的,没有的话还是会用基类的。


虚函数使用条件

virtual只能写在类的内部生命或者定义,不能写在外部,也就是外部的全局函数。
调用类的对象是无法使用虚函数的,必须使用基类指针来实现虚函数的调用
虚函数在派生类和基类中必须具有相同的及参数列表
虚函数在派生类和基类中返回值要求基本一致,但是当返回类型为该类型的指针和引用时除外
虚函数不能是函数模板


override 后缀可以强制要求检查函数是重载
final 后缀可以终止函数的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MoveObject{
public:
int x;
int y;
void virtual Move(){
x++;
y++;
}
};

class NPCObject :public MoveObject{
public:
void Move(){
x++;
y++;
std::cout << "NPCobject!\n";
}
};

学习了虚函数之后,我们知道继承的时候也会带上,那么NPC中的这个Move其实也就是一个虚函数,只不过我们再次重写了。

编译器也能看得到,当然这么写没太大关系,但是等NPC被别的类继承的时候,虽然函数还是叫move,但是转到上一层没看到virtual这个关键字还以为它不是虚函数。

所以这个也要看编程习惯,如果基类中他是虚函数,那么派生类尽量就都要加上,方便被其他类派生的时候可以直观的看出。

然后final这个关键字是用于结束重载的,所以像是虚函数后面定义了这个,那么派生类就不用加virtual了,都已经取消重载了自然没有必要性。

override呢就比较有意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MoveObject{
public:
int x;
int y;
void virtual Move(){
x++;
y++;
}
};

class NPCObject :public MoveObject{
public:
void Move(int a){
x++;
y++;
std::cout << "NPCobject!\n";
}
};

在正常情况下,NPC中的move待了参数,编译器可能会认为他是单独的存在。

可以看到编译器认为它是属于NPC类的成员。
但事实上我们是让他继承过来重写的,而不是造成无论是意义上还是代码上的问题

解决方法override就派上用场了,他会强制检查这个成员是不是继承过来的。


虚函数并不一定全部写在基类中,因为类可能不止派生一次,当派生了很多次的时候,难免会有一些有新的功能。


结语

虚函数在基类的private下,派生类就没法访问了,但虚函数在基类的public下,派生类改成private还是能用的,用的前期是通过指针,而不是直接.访问,毕竟派生类已经做成private。
但是在反复修改的时候其实间接的破坏了封装性,不利于维护。