cpp虚函数

多态

  1. 静态多态:相同对象接收到不同的消息,产生不同的结果 => 函数重载。
  2. 动态多态:不同对象接收到相同的消息,产生不同的结果 => 虚函数。

多态的作用就是为了提供高度统一的接口,实现代码复用,提高代码的可扩展性和可维护性。

虚函数

虚函数表

虚函数的实现原理可见另一文章:关于cpp虚函数表的实现原理

使用限制

0. virtual 具有继承性: 父类中定义为 virtual 的函数在子类中重写的函数也自动成为虚函数。需要注意的是,只有子类的虚函数和父类的虚函数定义完全一样才被认为是虚函数。

1. virtual 不能修饰类外的普通函数,只能修饰类中的成员函数(普通函数和析构函数)。

这句话很好理解,就是 virtual 关键字不能修饰全局函数。

1
2
3
virtual int test () { // 不正确,不能修饰全局函数
// do some thing
}

编译报错:

1
error: ‘virtual’ outside class declaration

2. virtual 不能修饰构造函数。

  • 从存储空间角度,虚函数的每个对象内部都有一个指向虚函数表的指针。那么虚函数表是什么时候初始化的呢?就是在调用构造函数的时候。cpp 调用 new 的步骤是,先申请所需的内存(malloc/operator new),再调用构造函数。如果构造函数是虚的,那必须通过虚函数表指针来找到虚构造函数的入口地址。然而此时申请的内存还没有初始化,不可能由虚函数表和虚函数表指针的。综上所述,构造函数不能为虚函数。
  • 从使用角度,构造函数是创建对象时调用的,不可能通过父类的指针或引用去调用。创建一个对象时,总是要明确指定对象的类型。但是析构函数不一样,可以通过基类指针进行析构。
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

class A {
public:
virtual A() {
cout << "construct" << endl;
}
};

int main() {
A* a = new A();
}

编译报错:

1
error: constructors cannot be declared ‘virtual’

3. virtual 不能修饰静态成员函数。

虚函数的调用时经过对象内部的虚函数表指针,找到虚函数表,进而去调用对应的函数。但是静态成员函数和实例没有关系,只和类有关。所以调用时不会隐式传入 this 指针。题外话,因为没有 this 指针,静态函数也不能声明为 const 和 volatile。因为 void func() const {} 会被转换为 void func(const A *this) {};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

class A {
public:
A() {
cout << "construct" << endl;
}
virtual static void print() {
cout << "print" << endl;
}
};

int main() {
A* a = new A();
a->print();
}

编译报错:

1
error: member ‘print’ cannot be declared both ‘virtual’ and ‘static’

4. virtual 不能修饰内联函数。

内联函数关键字 inline 是对编译器的一个建议:如果可能,请把此函数变为内联函数。虚函数是由虚函数表实现的,因为表中需要存放函数的地址。被内联展开的函数没有具体的地址,所以无法被 virtual 修饰。但是以下写法也不会报编译错误。

1
2
3
4
5
6
7
8
9
class A {
public:
A() {
cout << "construct" << endl;
}
virtual inline void print() {
cout << "print" << endl;
}
};

因为内联函数的建议编译器没有采纳,就和没有加 inline 关键字效果一样。

5. virtual 不能修饰友元函数。

友元函数是可以访问类内私有成员的非成员函数,是定义在类外的普通函数。它不属于某个类,所以自然无法被 virtual 修饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

class A {
public:
A() {
cout << "construct" << endl;
}
virtual friend void print();
};

void print() {
cout << "print" << endl;
}

int main() {
A* a = new A();
print();
}

编译报错:

1
error: virtual functions cannot be friends