今天偶然看到这个面试题,第一感觉是既然所有对象共享虚函数代码,那么虚函数指针对所有对象就是一样的,因此虚函数表也就是一样的,没有必要为每个对象复制一份一模一样的虚函数表。所以做个验证,在这儿记录一下。如有错误,欢迎留言指正,感谢感谢。

1. 单一虚函数表

验证代码如下:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
class A
{
public:A() { cout << "A()" << endl; }virtual void f() { cout << "f()" << endl; }virtual void g() { cout << "g()" << endl; }virtual ~A() { cout << "~A()" << endl; }
};typedef void(*Func)();int main()
{A a1, a2;ll* vptr;vptr = (ll*)*(ll*)(&a1);cout << "a1虚函数表地址: "<< vptr << endl;Func f = (Func)(*vptr);cout << "a1.f()地址: " << (ll*)f << endl;f();f = (Func)*(vptr+1);cout << "a1.g()地址: " << (ll*)f << endl;f();vptr = (ll*)*(ll*)(&a2);cout << "a2虚函数表地址: " << vptr << endl;f = (Func)(*vptr);cout << "a2.f()地址: " << (ll*)f << endl;f();f = (Func)*(vptr + 1);cout << "a2.g()地址: " << (ll*)f << endl;f();return 0;
}

输出结果:

VS2017 x64平台 Ubuntu1604 64位(g++5.4.0)
A()
A()
a1虚函数表地址: 00007FF7FB04ADB8
a1.f()地址:
00007FF7FB041456
f()
a1.g()地址: 00007FF7FB0414A1
g()
a2虚函数表地址: 00007FF7FB04ADB8
a2.f()地址: 00007FF7FB041456
f()
a2.g()地址: 00007FF7FB0414A1
g()
~A()
~A()
A()
A()
a1虚函数表地址:0x400f20
a1.f()地址: 0x400d3e
f()
a1.g()地址: 0x400d6a
g()
a2虚函数表地址: 0x400f20
a2.f()地址: 0x400d3e
f()
a2.g()地址: 0x400d6a
g()
~A()
~A()

输出f和g的地址以及对f和g进行调用是为了验证程序的正确性。无论是VS还是g++,两个对象的虚函数表地址都是一样的,说明两个对象共享虚函数表。

2. 多个虚函数表

既然单一虚函数表类的对象都共享虚函数表,那么无论继承类的对象有多少个虚函数表,这些虚函数表应该都是共享的。下面验证:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
class A
{
public:virtual void fA() { cout << "fA()" << endl; }virtual void gA() { cout << "gA()" << endl; }
};class B
{
public:virtual void fB() { cout << "fB()" << endl; }virtual void gB() { cout << "gB()" << endl; }
};class C: public A, public B
{
};typedef void(*Func)();int main()
{C c1, c2;ll* vptr;vptr = (ll*)*(ll*)(&c1);cout << "c1虚函数表1地址: "<< vptr << endl;Func f = (Func)(*vptr);cout << "c1.fA()地址: " << (ll*)f << endl;f();f = (Func)*(vptr+1);cout << "a1.gA()地址: " << (ll*)f << endl;f();vptr = (ll*)*(ll*)(&c2);cout << "c2虚函数表1地址: " << vptr << endl;f = (Func)(*vptr);cout << "a2.fA()地址: " << (ll*)f << endl;f();f = (Func)*(vptr + 1);cout << "a2.gA()地址: " << (ll*)f << endl;f();cout << endl;vptr = (ll*)*((ll*)(&c1)+1);cout << "c1虚函数表2地址: " << vptr << endl;f = (Func)(*vptr);cout << "c1.fB()地址: " << (ll*)f << endl;f();f = (Func)*(vptr + 1);cout << "a1.gB()地址: " << (ll*)f << endl;f();vptr = (ll*)*((ll*)(&c2)+1);cout << "c2虚函数表2地址: " << vptr << endl;f = (Func)(*vptr);cout << "a2.fB()地址: " << (ll*)f << endl;f();f = (Func)*(vptr + 1);cout << "a2.gB()地址: " << (ll*)f << endl;f();return 0;
}

输出如下:

VS2017 x64平台 Ubuntu1604 64位(g++5.4.0)
c1虚函数表1地址: 00007FF6BACCBE18
c1.fA()地址: 00007FF6BACC12F3
fA()
a1.gA()地址: 00007FF6BACC12B7
gA()
c2虚函数表1地址: 00007FF6BACCBE18
a2.fA()地址: 00007FF6BACC12F3
fA()
a2.gA()地址:00007FF6BACC12B7
gA()

c1虚函数表2地址: 00007FF6BACCBE38
c1.fB()地址: 00007FF6BACC1244
fB()
a1.gB()地址: 00007FF6BACC11DB
gB()
c2虚函数表2地址: 00007FF6BACCBE38
a2.fB()地址: 00007FF6BACC1244
fB()
a2.gB()地址: 00007FF6BACC11DB
gB()

c1虚函数表1地址: 0x401028
c1.fA()地址: 0x400d76
fA()
a1.gA()地址: 0x400da2
gA()
c2虚函数表1地址: 0x401028
a2.fA()地址: 0x400d76
fA()
a2.gA()地址: 0x400da2
gA()

c1虚函数表2地址: 0x401048
c1.fB()地址: 0x400dce
fB()
a1.gB()地址: 0x400dfa
gB()
c2虚函数表2地址: 0x401048
a2.fB()地址: 0x400dce
fB()
a2.gB()地址: 0x400dfa
gB()

可以看出,c1和c2的两个虚函数表地址仍然是一样的。

结论:如果一个类有虚函数,那么这个类的所有对象共享一个虚函数表。

C++单个类的所有对象是否共享虚函数表的验证相关推荐

  1. C++ 虚函数表是属于类的还是属于对象的

    虚函数表到底是一个对象一个,还是一个类一个.从节省内存的角度上说,应该是一个类一个,同一个类的不同对象拥有相同虚函数表.我们用代码来试一试 class classA { virtual void fu ...

  2. C++对象内存布局--①测试虚函数表属于类

    C++对象内存布局--①测试虚函数表属于类 测试1:同一个类的多个对象共享同一张虚函数表.   //虚函数表.cpp //2010年8月18日 //测试虚函数表,说明虚函数表属于类所有.同一个类的多个 ...

  3. C++对象的内存布局1---基础篇----C++ 虚函数表解析

    [-] 前言 虚函数表 一般继承(无虚函数覆盖) 一般继承(有虚函数覆盖) 多重继承(无虚函数覆盖) 多重继承(有虚函数覆盖) 安全性 结束语 附录一:VC中查看虚函数表 附录 二:例程 前言 C++ ...

  4. Cpp 对象模型探索 / 对象的虚函数表指针的位置

    一.源码 #include <iostream>class A { public:virtual void func(){};public:int count_ = 0; };int ma ...

  5. 通过对象指针的方式强行指定到子类_C++中的虚指针与虚函数表

    ​ 最近在逛B站的时候发现有候捷老师的课程,如获至宝.因此,跟随他的讲解又复习了一遍关于C++的内容,收获也非常的大,对于某些模糊的概念及遗忘的内容又有了更深的认识. 以下内容是关于虚函数表.虚函数指 ...

  6. 类的不同实例化使用一张虚函数表

    1 说明 众所周知,每个带有虚函数的类,或者继承具有虚函数类的对象,本身都是会有一个虚函数表的,前者为自身创建的,后者为继承,且虚函数表的指针位置位于类的首四位地址,如下图所示! 如何获取虚函数表地址 ...

  7. 【C++】 类的内存对齐、虚函数表

      本文分为以下几个部分内容: 什么是内存对齐,为什么要内存对齐 C++的空类,以及没有虚函数和非静态变量的类 C++类的内存分布(成员变量) C++类的内存分布(虚函数) 一个类的情况 继承关系中的 ...

  8. C++中的类对象的内存分布以及虚函数表内存分布

    ​ 目录 目录 一.前言 二.C++ 类对象的内存布局 三.函数调用 一.前言 C++的精髓是虚函数.虚函数带来的好处就是:可以定义一个基类的指针,其指向一个继承类,当通过基类的指针去调用函数时,可以 ...

  9. C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中

    C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中 测试2:证明派生类的虚函数的地址跟第一基类的虚函数地址保存在同一张虚函数表中. 派生类有多少个拥有虚函数的基类,派生类对象就有多少 ...

最新文章

  1. cordova 环境配制和创建插件
  2. 速度、准确率与泛化性能媲美SOTA CNN,Facebook开源高效图像Transformer
  3. 生成pojo mysql_通过数据库表反向生成pojo类
  4. 北京协和医学院823计算机原理,2017年北京协和医学院放射医学研究所(天津)823计算机原理考研题库...
  5. How to check accessibility errors via Web IDE
  6. 我的2018知乎大数据分析
  7. Django(模板语言-自定义filter和simple_tag)
  8. 前端学习(2515):父向子传值
  9. play @Before 的使用
  10. 全球首发联发科天玑1000+手机发布:售价2198元起!
  11. 【iOS开发】使用XCode 8进行真机调试的方法(各种报错信息的解决方法)
  12. Android Studio导入model
  13. 添加负载机_从 Coco 转向 Koko,Jumpserver 堡垒机 V1.5.3 发布
  14. 女友的一个建议,让26岁程序员做了个价值 10 亿美元的 App
  15. 写的网页标题乱码,怎么办?
  16. VB代码 VB小程序
  17. 【日常科普】浏览器网页视频自定义倍速播放(无需任何插件)
  18. [LiteratureReview]A Collaborative Visual SLAM Framework for Service Robots
  19. Zookeeper之ZAB协议详解
  20. 大数据预处理之数据集成

热门文章

  1. 各种Excel VBA的命令
  2. Zhong__k8s基础尝试和配置flannel
  3. 集合类超级无敌史无前例的超详细总结
  4. 计算机图学实验报告,计算机图形学实验报告实验1
  5. 1124——Vue+SpringBoot+Mybatis的简单员工管理项目
  6. 阿里面经总结:阿里三面+交叉面+hr面试
  7. nimi SearchEngin 项目思路及算法
  8. 天气预报地区id列表
  9. 渗透测试-地基篇-美杜莎Medusa(十二)
  10. 【论文阅读】基于区块链的无人集群作战信息共享架构_臧义华