虚函数、虚函数表
使用结构体表示对象,状态通过属性表示,行为通过函数指针表示。
如果对象的行为都一致,每个对象都会初始化它的函数指针,这样会导致内存浪费。
看下面这个例子:
1 typedef struct Foo { 2 int count; 3 void (* const func0)(struct Foo *pThis); 4 void (* const func1)(struct Foo *pThis); 5 void (* const func2)(struct Foo *pThis); 6 } Foo; 7 // 根据对象定义生成几个对象 8 Foo foo0 = {0, func0_impl, func1_impl, func2_impl}; 9 Foo foo1 = {1, func0_impl, func1_impl, func2_impl}; 10 Foo foo2 = {2, func0_impl, func1_impl, func2_impl};
出现以下情况会导致浪费内存
- 有多个对象都具有相同的行为
- 有较多的函数指针
- 需要生成较多数量的对象
虚函数:通过函数指针实现,根据对象初始化不同而展现不同功能。
虚函数表:虚函数的集合,如下所示:
typedef struct FooVtbl { void (* const func0)(struct Foo *pThis); void (* const func1)(struct Foo *pThis); void (* const func2)(struct Foo *pThis); } FooVtbl; typedef struct Foo { const int count; const FooVtbl * const pVtbl; } Foo; int main() { static FooVtbl foo_vtbl = {func0_impl, func1_impl, func2_impl}; Foo foo0 = {0, &foo_vtbl}; Foo foo1 = {1, &foo_vtbl}; Foo foo2 = {2, &foo_vtbl}; foo0.pVtbl->func0(&foo0); return EXIT_SUCCESS; }
这就是虚函数表减少内存浪费的方式,优缺点如下:
- 代码结构中,仅需要对象持有指向虚函数表的指针即可,无需持有函数指针,可节约内存。
- 由于调用会经过虚函数表,程序的结构变复杂了。
非虚函数
对象内持有的函数指针为虚函数,它可以根据对象的不同而使其行为发生变化。但如果函数在不同对象中的行为是相同的,对象中就无需持有函数指针了。例如
typedef struct FooVtbl { void (* const func0)(struct Foo *pThis); void (* const func1)(struct Foo *pThis); void (* const func2)(struct Foo *pThis); } FooVtbl; typedef struct Foo { const int count; FooVtbl *pVtbl; void (*reset_counter)(struct Foo *pThis); } Foo;
优点
- 节省内存。
缺点
- 无法根据对象动态的改变其行为。
- 命名空间问题。
建议:优先使用函数指针,只有在内存有限、对象行为不会变化的情况下等,才考虑使用非虚函数。