C++ 链式继承下的虚函数列表

目录

1.虚函数列表的位置

2.虚函数列表的内容

3.链式继承中虚函数列表的内容

?

注:

虚函数列表 又称为虚表, vtbl , 指向它的指针称为vptr, vs2019中称为__vfptr

操作系统: windows 10 专业版 64位

编译器: Visual Studio 2019 Community

?

1.虚函数列表的位置

结论

编译器一般会保证指向虚函数列表的指针存在于对象实例中最前面的位置

而虚函数列表中的内容, 就是多个函数指针

代码验证:

首先声明一个基类Base和一个派生类Derived

class  Base
{
public:
  virtual void f() { std::cout << "Base1::f" << std::endl; }
  virtual void g() { std::cout << "Base1::g" << std::endl; }
  virtual void h() { std::cout << "Base1::h" << std::endl; }
  virtual void i() { std::cout << "Base1::i" << std::endl; }
};

class Derived : public Base
{
  virtual void g() override { std::cout << "Derived::g" << std::endl; }
  virtual void h1() { std::cout << "Derived::i1" << std::endl; }
};

然后实例化一个派生类的对象

Derived derived;

现在我们打印出该对象的地址

std::cout << "derived对象的地址: " << (&derived) << std::endl;

由于我们假定指向虚函数列表的<指针>存在于对象实例中最前面的位置

那么我们可以认定, derived对象的地址中的开头是一个指针的地址(称之为指针pA)

而这个指针(pA)指向虚函数列表中的开头, 也就是一个函数指针(称之为指针pF)

所以这个指针(pA), 是一个指向指针的指针, 即指向指针(pF)的指针(pA)

基于这个推测, 我们将derived对象的地址指针pA的地址进行一个类型转换

使用reinterpret_cast<int**>关键字, 将其转换为一个指向指针的指针

reinterpret_cast<int**>(&derived)

现在我们对这个指针(pA)的地址, 取其内容, 就会得到pA的内容

std::cout << "derived对象中第一个指针的内容: " << *reinterpret_cast<int**>(&derived) << std::endl;

根据上面的推测, 这个内容, 就是虚函数列表的地址

控制台输出如下:

通过vs2019中, 可以直接查看到derived的__vfptr对象的地址, 和控制台打印的内容是相同的

?

2.单继承中虚函数列表的内容

基类中有4个函数, 分别为

f();
g();
h();
i();

派生类中有2个函数,分别为

g();
i1();

现在使用表格的方式表示出来, 方便查看, 进行了override的函数, 会放在同一行

结论 在虚函数列表中, 函数的布局如下图所示:

代码验证请看链式继承中虚函数列表的内容

?

3.链式继承中虚函数列表的内容

声明3个类, 其继承关系为Derived继承Base2, Base2继承Base1

class Base1 {
public:
  virtual void f() { std::cout << "Base1::f" << std::endl; }
  virtual void g() { std::cout << "Base1::g" << std::endl; }
  virtual void h() { std::cout << "Base1::h" << std::endl; }
  virtual void i() { std::cout << "Base1::i" << std::endl; }
};

class Base2 : public Base1{
public:
  virtual void f()override { std::cout << "Base2::f" << std::endl; }
  virtual void h1() { std::cout << "Base2::h1" << std::endl; }
};

class Derived : public Base2 {
public:
  virtual void g()override { std::cout << "Derived::g" << std::endl; }
  virtual void i1() { std::cout << "Derived::i1" << std::endl; }
};

用表格的方法表示为:

!()(https://silenzio-markdown-image-hosting-service.oss-cn-beijing.aliyuncs.com/%E5%8D%9A%E5%AE%A2%E5%9B%BE%E5%BA%8A/C%2B%2B%20%E9%93%BE%E5%BC%%E7%BB%A7%E6%89%BF%E4%B8%8B%E7%9A%84%E8%99%9A%E5%87%BD%E6%95%B0%E5%88%97%E8%A1%A8/1a885406e3318ccbbf5dce9961e1599.png)

结论

在虚函数列表中, 函数的布局如下图所示:

Derive只有一个虚函数表, 是在Base2的虚函数表上, 进行类似于单继承的覆盖
同理, Base2也有一张虚函数表, 是在Base1的虚函数表上, 进行单继承的覆盖

代码验证

////////////////////////////////////////////////////////////////////////////////
// 链式继承
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
class Base1 {
public:
  virtual void f() { std::cout << "Base1::f" << std::endl; }
  virtual void g() { std::cout << "Base1::g" << std::endl; }
  virtual void h() { std::cout << "Base1::h" << std::endl; }
  virtual void i() { std::cout << "Base1::i" << std::endl; }
};

class Base2 : public Base1{
public:
  virtual void f()override { std::cout << "Base2::f" << std::endl; }
  virtual void h1() { std::cout << "Base2::h1" << std::endl; }
};

class Derived : public Base2 {
public:
  virtual void g()override { std::cout << "Derived::g" << std::endl; }
  virtual void i1() { std::cout << "Derived::i1" << std::endl; }
};

using Fun = void(*)(void);

int main()
{
  Fun pFun = nullptr;

  // 操作系统: windows 10 专业版 32/64位都可以
  // 编译器 : Visual Studio 2019 Community
  std::cout << sizeof(int) << std::endl; //  32位:4  64位:4
  std::cout << sizeof(long) << std::endl; // 32位:4  64位:4
  std::cout << sizeof(int*) << std::endl; // 32位:4  64位:8

  std::cout << "-------------------------------------------------------------------------------------- " << std::endl;
  std::cout << "Base1的虚表如下 " << std::endl;
  Base1 base1;

  std::cout << "base1对象的地址: " << (&base1) << std::endl;
  std::cout << "base1对象中第一个指针的地址(不是内容): " << (&base1) << std::endl;
  // &base1 是一个指向指针的指针
  // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个虚表(虚表的内容也是一堆指针)
  std::cout << "base1对象中第一个指针的内容: " << *reinterpret_cast<int**>(&base1) << std::endl;
  std::cout << "base1虚函数表地址: " << *reinterpret_cast<int**>(&base1) << std::endl;

  // 虚函数表地址, 也是一个指向指针的指针
  // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个函数指针
  std::cout << "base1虚函数表 — 第一个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))))) << std::endl;
  pFun = reinterpret_cast<Fun>(* (reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)))));
  std::cout << "base1虚函数表 — 第一个函数内容:";
  pFun();                        // base1::f
  std::cout << std::endl;

  // 注意次数的偏移量, 32位偏移量是+1, 64位偏移量是+2
  std::cout << "base1虚函数表 — 第二个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))+1*(sizeof(int*)/sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 1 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base1虚函数表 — 第二个函数内容:";
  pFun();                        // base1::g
  std::cout << std::endl;

  std::cout << "base1虚函数表 — 第三个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 2 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base1虚函数表 — 第三个函数内容:";
  pFun();                        // base1::h
  std::cout << std::endl;

  std::cout << "base1虚函数表 — 第四个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 3 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base1虚函数表 — 第四个函数内容:";
  pFun();                        // base1::i
  std::cout << std::endl;

  std::cout << "-------------------------------------------------------------------------------------- " << std::endl;
  std::cout << "Base2的虚表如下 " << std::endl;
  Base2 base2;

  std::cout << "base2对象的地址: " << (&base2) << std::endl;
  std::cout << "base2对象中第一个指针的地址(不是内容): " << (&base2) << std::endl;
  // &base1 是一个指向指针的指针
  // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个虚表(虚表的内容也是一堆指针)
  std::cout << "base2对象中第一个指针的内容: " << *reinterpret_cast<int**>(&base2) << std::endl;
  std::cout << "base2虚函数表地址: " << *reinterpret_cast<int**>(&base2) << std::endl;

  // 虚函数表地址, 也是一个指向指针的指针
  // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个函数指针
  std::cout << "base2虚函数表 — 第一个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)))));
  std::cout << "base2虚函数表 — 第一个函数内容:";
  pFun();                        // base2::f
  std::cout << std::endl;

  std::cout << "base2虚函数表 — 第二个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 1 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 1 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base2虚函数表 — 第二个函数内容:";
  pFun();                        // base1::g
  std::cout << std::endl;

  std::cout << "base2虚函数表 — 第三个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 2 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base2虚函数表 — 第三个函数内容:";
  pFun();                        // base1::h
  std::cout << std::endl;

  std::cout << "base2虚函数表 — 第四个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 3 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base2虚函数表 — 第四个函数内容:";
  pFun();                        // base1::i
  std::cout << std::endl;

  std::cout << "base2虚函数表 — 第五个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 4 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 4 * (sizeof(int*) / sizeof(int)))));
  std::cout << "base2虚函数表 — 第五个函数内容:";
  pFun();                        // base2::h1
  std::cout << std::endl;

  std::cout << "-------------------------------------------------------------------------------------- " << std::endl;
  std::cout << "Derived的虚表如下 " << std::endl;
  Derived Derived;

  std::cout << "Derived对象的地址: " << (&Derived) << std::endl;
  std::cout << "Derived对象中第一个指针的地址(不是内容): " << (&Derived) << std::endl;
  // &base1 是一个指向指针的指针
  // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个虚表(虚表的内容也是一堆指针)
  std::cout << "Derived对象中第一个指针的内容: " << *reinterpret_cast<int**>(&Derived) << std::endl;
  std::cout << "Derived虚函数表地址: " << *reinterpret_cast<int**>(&Derived) << std::endl;

  // 虚函数表地址, 也是一个指向指针的指针
  // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个函数指针
  std::cout << "Derived虚函数表 — 第一个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)))));
  std::cout << "Derived虚函数表 — 第一个函数内容:";
  pFun();                        // base2::f
  std::cout << std::endl;

  std::cout << "Derived虚函数表 — 第二个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 1 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 1 * (sizeof(int*) / sizeof(int)))));
  std::cout << "Derived虚函数表 — 第二个函数内容:";
  pFun();                        // Derived::g
  std::cout << std::endl;

  std::cout << "Derived虚函数表 — 第三个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 2 * (sizeof(int*) / sizeof(int)))));
  std::cout << "Derived虚函数表 — 第三个函数内容:";
  pFun();                        // base1::h
  std::cout << std::endl;

  std::cout << "Derived虚函数表 — 第四个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 3 * (sizeof(int*) / sizeof(int)))));
  std::cout << "Derived虚函数表 — 第四个函数内容:";
  pFun();                        // base1::i
  std::cout << std::endl;

  std::cout << "Derived虚函数表 — 第五个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 4 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 4 * (sizeof(int*) / sizeof(int)))));
  std::cout << "Derived虚函数表 — 第五个函数内容:";
  pFun();                        // base2::h1
  std::cout << std::endl;

  std::cout << "Derived虚函数表 — 第六个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 5 * (sizeof(int*) / sizeof(int))))) << std::endl;
  pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 5 * (sizeof(int*) / sizeof(int)))));
  std::cout << "Derived虚函数表 — 第六个函数内容:";
  pFun();                        // Derived::i1
  std::cout << std::endl;

  return 0;
}

原文地址:https://www.cnblogs.com/silenzio/p/12205888.html

时间: 2024-10-12 06:15:42

C++ 链式继承下的虚函数列表的相关文章

C++ 对象的内存布局—— 虚继承下的虚函数

C++ 对象的内存布局(下)这篇文章的"单一虚拟继承"和"钻石型虚拟继承"时的类内存布局讲得不太清楚,我有一处疑问,我用的是VS2005.因此记录一下. 类继承图例如以下: 这里:类B被类B1和B2虚拟继承,而B1和B2同一时候被D继承. B1的f().B2的f()覆盖了B的f(): D的f()覆盖了B1的f(),D的f1()覆盖了B1的f1() D的f()覆盖了B2的f(),D的f2()覆盖了B2的f2() 类代码例如以下: class B { public: i

C++链式继承

        继承,对于学习C++的每一个人来说,都不会陌生.在Qt的开发中,如果你需要对一个无边框的界面支持move操作,那么你就得通过继承重写虚函数来实现,这并不难,但如果我还需要对一个按钮支持移动,一般情况,当然是Crtl + c .Crtl + v搞定,但我们不难发现,对于move这个操作来说,其实代码完全一模一样,那么有没有什么办法可以简化,可以一劳永逸呢?         答案是肯定的,这里我们就需要用到C++的模板来实现了,即本文要介绍的链式继承.         前面有实现过m

虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte

#include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout << "A:A" <<endl; } virtual void getb(){ cout << "A:B" <<endl; } }; class B :public A{ public: B(){} virtual void g

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)

在VS2013编程,调试 问题 :  菱形继承会引来,二义性 1.源代码 </pre><pre name="code" class="cpp">#include <iostream> using namespace std; class Base { public: virtual void FunTest() { cout << "Base::FunTest () " << endl;

谈谈c++中继承中的虚函数

c++继 承中的虚函数 c++是一种面向对象的编程语言的一个很明显的体现就是对继承机制的支持,c++中继承分很多种,按不同的分类有不同分类方法,比如可以按照基类的个数分为多继承和单继承,可以按照访问权限分为public继承.protected继承和private继承,按照是否是虚拟继承可以分为virtual继承和non-virtual继承.当然这里的分类标准都是有重叠的部分,比如,non-virtual继承又可以分为单继承和多继承.这里要讨论的是虚函数,因此主要从virtual和non-virt

C++中虚函数的理解,以及简单继承情况下的虚函数的表!

面向对象的三大特征=封装性+继承性+多态性 封装=将客观事物抽象成类,每个类对自身的数据和方法实行权限的控制 继承=实现继承+可视继承+接口继承 多态=将父类对象设置成为和一个或者更多它的子对象相等的技术, 用子类对象给父类对象赋值之后, 父类对象就可以根据当前赋值给它的子对象的特性一不同的方式运作 C++的空类有哪些成员函数 1.缺省构造函数 2.缺省拷贝构造函数 3.缺省析构函数 4.缺省赋值运算符 5.缺省取址运算符 6.缺省取址运算符const PS:只有当实际使用的时候才会去使用这些类

继承中的虚函数、纯虚函数、普通函数

一.虚函数 被virtual关键字修饰的类成员函数就是虚函数.虚函数的作用就是实现运行时的多态性,将接口与实现分离.简单理解就是相同函数有着不同的实现,但因个体差异而采用不同的策略. 基类中提供虚函数的实现,为派生类提供默认的函数实现.派生类可以重写基类的虚函数以实现派生类的特殊化.如下: class Base{ public: virtual void foo() { cout<<"Base::foo() is called"<<endl; } }; clas

C++继承后的虚函数访问权限

今天在写代码时发现对继承后的函数访问权限不太清楚,于是自己做了个测试: 1.头文件(test.h) 1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 private: 6 void print(){ 7 cout << "this is A" << endl; 8 } 9 }; 10 11 class B:public A{ }; A为基类,B为A的子类. 2.源文件(test.c

C++之虚函数的原理

1. 看看一个类中如果有虚函数,它的内存布局: class A{ double i; int j; virtual void foo(){} virtual void fun(){} }; 内存布局: 1> class A size(24): 1> +--- 1> 0 | {vfptr} 1> 8 | i 1> 16 | j 1> | <alignment member> (size=4) 1> +--- 可以看出,A中有一个vfptr,这个是指向一个