Lecture 5
虚函数
一般的Override:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A
{ public:
void f();
};
class B: public A
{ public:
void f();
void g();
};
......
A a;
B b;
a.f(); //A的f
b.f(); //B的f
b.A::f(); //A的f
静态绑定
但是,基类的指针或引用可以指向或引用基类对象,也可以指向或引用派生类对象,此时调用哪个函数?
cpp默认静态绑定,可以大致参考java的static type,但是和java不同的是,java会在runtime不断改变dynamic type,cpp却不会。但总之,这里b的类型按照静态绑定的A来算,所以是A的函数。
虚函数
在C++中,在基类中用虚函数来实现动态绑定。
1
2
3
4
5
6
7
8
9
10
11
class A
{ int x,y;
public:
virtual void f(); //虚函数
};
class B: public A
{ int z;
public:
void f();
void g();
};
所有带”virtual”关键字的函数,调用时都以动态绑定的为主。
虚函数有两个作用:
•指定消息采用动态绑定。
•指出基类中可以被派生类重定义的成员函数。
要注意签名必须和基类中带”virtual”的函数相同的函数,才能有效override
1
2
3
4
5
6
7
8
9
10
11
12
class A
{ int x,y;
public:
virtual void f();
};
class B: public A
{ int z;
public:
void f(); //对A中f的重定义
void f(int); //新定义的成员函数
void g();
};
几点说明
- 只有类的成员函数才可以是虚函数,但静态成员函数不能是虚函数。
- 构造函数不能是虚函数,析构函数可以(往往)是虚函数。
- 只要在基类中说明了虚函数,在派生类、派生类的派生类、…中,同型构的成员函数都是虚函数(virtual可以不写)。
- 只有通过指针或引用访问对象类的虚函数时才进行动态绑定。
- 基类的构造函数和析构函数中对虚函数的调用不进行动态绑定。
虚函数的应用情况
要把析构函数定义为虚函数的原因:
虚函数动态绑定的实现原理
如果没有虚函数,就不会有虚函数表。
虚函数表的指针位于每个对象的开头,虚函数表的内存地址固定,一个类只有一个虚函数表。
抽象类
纯虚函数
纯虚函数是没给出实现的虚函数,函数体用“=0”表示, 例如:
1
2
3
4
5
6
class A
{ ......
public:
virtual int f()=0; //纯虚函数
......
};
抽象类
包含纯虚函数的类称为抽象类。抽象类不能用于创建对象。抽象类的作用是为派生类提供一个基本框架和一个公共的对外接口。
1
2
3
4
5
6
7
8
9
class A //抽象类
{ ......
public:
virtual int f()=0; //纯虚函数
......
};
......
A a; //Error,A是抽象类
抽象类可以防止如下骚操作(玩cpp玩的)
由于在C++中使用某个类时必须要见到该类的定义,因此,使用者能够见到该类的非public成员,这样就有手段绕过类的访问控制而使用类的非public成员。
到底哪个傻逼会这么用…
本文由作者按照 CC BY 4.0 进行授权