文章

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

静态绑定

但是,基类的指针或引用可以指向或引用基类对象,也可以指向或引用派生类对象,此时调用哪个函数?

image-20231010151909785

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();
};

image-20231010151956586

所有带”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();
};

几点说明

  1. 只有类的成员函数才可以是虚函数,但静态成员函数不能是虚函数。
  2. 构造函数不能是虚函数,析构函数可以(往往)是虚函数。
  3. 只要在基类中说明了虚函数,在派生类、派生类的派生类、…中,同型构的成员函数都是虚函数(virtual可以不写)。
  4. 只有通过指针或引用访问对象类的虚函数时才进行动态绑定。
  5. 基类的构造函数和析构函数中对虚函数的调用不进行动态绑定。

虚函数的应用情况

image-20231010152835086

要把析构函数定义为虚函数的原因:

image-20231010153014980

虚函数动态绑定的实现原理

image-20231010153156538

如果没有虚函数,就不会有虚函数表。

虚函数表的指针位于每个对象的开头,虚函数表的内存地址固定,一个类只有一个虚函数表。

抽象类

纯虚函数

纯虚函数是没给出实现的虚函数,函数体用“=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成员。

image-20231010153807730

image-20231010153850171

到底哪个傻逼会这么用…

本文由作者按照 CC BY 4.0 进行授权