C++ 基础教程之虚函数实例代码详解

2020-02-22 21:57:52丽君

为了验证上面所述我们可以做一组对照,首先我们用 gnu 工具 nm 来查看 sysbols,可以发现如下的部分:

$ nm virtual.exe | grep -c -E "plus|base"

然后我们改造一下上面的代码:

class base {
public:
 void name(){printf("basen");}; // 修改
 virtual ~base(){};
};
class plus: public base {
public:
 void name(){printf("plusn");}; // 修改
};

编译后重新执行 nm 命令:

nm virtual_.exe | grep -c -E "plus|base" 45

经过比对后我们会发现修改后缺少了以下symbols:

000000000040509c p .pdata$_ZN4plus4nameEv 0000000000402e00 t .text$_ZN4plus4nameEv 00000000004060a0 r .xdata$_ZN4plus4nameEv 0000000000402e00 T _ZN4plus4nameEv

动态联编在效率上要低于静态联编,在C++ 中默认使用静态联编。C++ 之父strousstup 认为 C++ 指导原则之一是不要为不使用的特性付出代价(cpu、memory等)。

所以在派生类不需要去重写基类函数时,则不要将其声明为virtual函数。

virtual 函数工作原理

虚函数表示每一个使用C++的开发者耳熟能详的东西,有一个道经典的试题如下:

#include <stdio.h>
class base
{
public:
 base(){};
 virtual ~base() { printf("basen"); };
};
class plus : public base
{
public:
 plus(/* args */){};
 virtual ~plus() { printf("plusn"); };
};
class plus2 : public base
{
public:
 plus2(/* args */){};
 ~plus2() { printf("plus2n"); };
};
class plus3 : public base
{
public:
 virtual void name() { printf("plus3"); };
 plus3(/* args */){};
 virtual ~plus3() { printf("plus3n"); };
};
class empty
{
private:
 /* data */
public:
 empty(/* args */){};
 ~empty() { printf("emptyn"); };
};
int main()
{
 base b;
 printf("base: %dn", sizeof(b));
 plus p;
 printf("plus: %dn", sizeof(p));
 plus2 p2;
 printf("plus2: %dn", sizeof(p2));
 plus3 p3;
 printf("plus3: %dn", sizeof(p3));
 empty e;
 printf("empty: %dn", sizeof(e));
}

其最终输出的结果如下:

base: 8 plus: 8 plus2: 8 plus3: 8 empty: 1 empty plus3 base plus2 base plus base base

ps: 由于操作系统位数的影响结果可能有变动,在x64位系统中指针内存分配大小为 8 字节,x86 系统中指针内存分配大小为 4。

我们可以清楚的看到,只要存在虚函数不论是成员函数异或是析构函数,是在类中定义或继承都会有包含一个虚函数表。而这里的8字节就是分配给了虚函数表的指针。

我们可以通过gnu tool gdb 指令进行验证,在触发断点之后通过 info local 命令去查看:

(gdb) info locals
b = {_vptr.base = 0x555555755d20 <vtable for base+16>}
p = {<base> = {_vptr.base = 0x555555755d00 <vtable for plus+16>}, <No data fields>}
p2 = {<base> = {_vptr.base = 0x555555755ce0 <vtable for plus2+16>}, <No data fields>}
p3 = {<base> = {_vptr.base = 0x555555755cb8 <vtable for plus3+16>}, <No data fields>}
e = {<No data fields>}