首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的虚拟继承

C++中的虚拟继承
EN

Stack Overflow用户
提问于 2010-07-04 16:50:22
回答 3查看 2.1K关注 0票数 3

我当时正在阅读关于虚拟继承的维基百科文章。我看了整篇文章,但我真的跟不上最后一段。

这是通过为哺乳动物和WingedAnimal提供一个vtable指针(或“v指针”)来实现的,因为,例如,在哺乳动物和它的动物部分的开头之间的内存偏移直到运行时才是未知的。这样蝙蝠就变成了(动物,哺乳动物,动物,WingedAnimal,蝙蝠,动物)。有两个vtable指针,每个继承层次结构都有一个,它们实际上继承了pointers。在本例中,一个用于哺乳动物,另一个用于WingedAnimal。因此,对象的大小增加了两个指针,但是现在只有一个动物,没有歧义。Bat类型的所有对象都将具有相同的指针,但每个Bat对象将包含其自己的唯一动物对象。如果另一个类继承了哺乳动物,如松鼠,那么松鼠中的哺乳动物对象中的v指针将与蝙蝠中的哺乳动物对象中的v指针不同,尽管它们在特殊情况下仍然可以是相同的,即对象的松鼠部分与蝙蝠部分具有相同的大小,因为从哺乳动物到动物部分的距离是相同的。vtables并不是完全相同的,但是它们中的所有基本信息(距离)都是。

能不能再给我点线索。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-07-04 19:08:44

有时,您确实需要看到一些代码/图表:)注意,标准中没有提到这个实现细节。

首先,让我们看看如何在C++中实现方法:

代码语言:javascript
复制
struct Base
{
  void foo();
};

这类似于:

代码语言:javascript
复制
struct Base {};

void Base_foo(Base& b);

事实上,当您查看调试器中的方法调用时,您通常会看到this参数是第一个参数。它有时被称为隐式参数。

现在,转到虚拟表上。在C和C++中,可以有指向函数的指针。vtable本质上是指向函数的指针表:

代码语言:javascript
复制
struct Base
{
  int a;
};

void Base_set(Base& b, int i) { b.a = i; }
int Base_get(Base const& b) { return b.a; }

struct BaseVTable
{
  typedef void (*setter_t)(Base&, int);
  typedef int (*getter_t)(Base const&);

  setter_t mSetter;
  getter_t mGetter;

  BaseVTable(setter_t s, getter_t g): mSetter(s), mGetter(g) {}
} gBaseVTable(&Base_set, &Base_get);

现在我可以做这样的事情:

代码语言:javascript
复制
void func()
{
  Base b;
  (*gBaseVTable.mSetter)(b, 3);
  std::cout << (*gBaseVTable.mGetter)(b) << std::endl; // print 3
}

现在,继续继承。让我们创建另一个结构

代码语言:javascript
复制
struct Derived: Base {}; // yeah, Base does not have a virtual destructor... shh

void Derived_set(Derived& d, int i) { d.a = i+1; }

struct DerivedBaseVTable
{
  typedef void (*setter_t)(Derived&,int);
  typedef BaseVTable::getter_t getter_t;

  setter_t mSetter;
  getter_t mGetter;

  DerivedBaseVTable(setter_t s, getter_t g): mSetter(s), mGetter(g) {}
} gDerivedBaseVTable(&Derived_set, &Base_get);

以及使用:

代码语言:javascript
复制
void func()
{
  Derived d;
  (*gDerivedBaseVTable.mSetter)(d, 3);
  std::cout << (*gDerivedBaseVTable.mGetter)(d) << std::endl; // print 4
}

但是如何实现自动化呢?

  • 您只需要每个类至少有一个虚拟函数的vtable实例。
  • 类的每个实例都将包含一个指向vtable的指针,作为其第一个属性(即使您不能单独访问它)。

那么,在多重继承的情况下会发生什么呢?在内存布局方面,继承非常类似于组合:

代码语言:javascript
复制
|                                     Derived                                   |
|                 BaseA                 |                 BaseB                 |
| vpointer | field1 | field2 | padding? | vpointer | field1 | field2 | padding? |

因此,MostDerived将有两个虚拟表:一个用于更改BaseA中的方法,另一个用于更改BaseB中的方法。

纯虚拟函数通常表示为相应字段中的空指针(简单地)。

最后,建筑和破坏:

施工

  • 构造BaseA:首先初始化v指针,然后执行属性,然后执行构造函数的主体。
  • BaseB是构建的:v指针、属性、体
  • Derived是构建的:替换指针(都是)、属性、主体

破坏

  • Derived is destructed:析构函数的主体,销毁属性,将基本指针放回
  • BaseB被破坏:身体,属性
  • BaseA被破坏:身体,属性

我认为这是相当全面的,我会很高兴,如果有一些C++大师可以审查这一点,并检查我没有犯任何愚蠢的错误。另外,如果有什么遗漏了,我很乐意补充一下。

票数 6
EN

Stack Overflow用户

发布于 2010-07-04 17:06:45

我不能真的。本节试图描述如何在C++实现中使用虚拟方法表来提供动态绑定(在多重继承的情况下)。

如果你没有做编译器,我的建议是:不用麻烦。阅读您最喜欢的关于继承、虚拟方法、多重继承和虚拟继承的C++书籍。

另外,C++标准(IIRC)不需要使用vtable,而是实现细节。所以说真的,别费心了。

票数 6
EN

Stack Overflow用户

发布于 2010-07-04 17:13:30

正如mkluwe所建议的,指针实际上不是语言的一部分。然而,了解实现技术可能是有用的,特别是在像C++这样的低级语言中。

如果你真的想学这个,我会推荐在C++对象模型中,它解释了这个和很多其他细节。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3175437

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档