C++ 子类中的这种更改是否需要重新编译依赖于超类的代码?

C++ 子类中的这种更改是否需要重新编译依赖于超类的代码?,c++,g++,vtable,C++,G++,Vtable,最近我学习了一些关于虚拟桌子的更深入的东西,这个问题浮现在我的脑海中 假设我们有这个样本: class A { virtual void foo(); } class B : public A { void foo(); } 在这种情况下,据我所知,每个类都会有一个vtable,分派将非常简单 现在假设我们将B类更改为如下内容: class B : public C, public A { void foo(); } void bar(A * a) { a->foo();

最近我学习了一些关于虚拟桌子的更深入的东西,这个问题浮现在我的脑海中

假设我们有这个样本:

class A {
 virtual void foo();
}

class B : public A {
 void foo();
}
在这种情况下,据我所知,每个类都会有一个vtable,分派将非常简单

现在假设我们将B类更改为如下内容:

class B : public C, public A {
  void foo();
}
void bar(A * a) {
  a->foo();
}

如果类C有一些虚拟方法,那么B的调度机制将更加复杂。两个继承路径B-C、B-A等可能都有2个vtable

从我到目前为止所了解到的情况来看,如果代码库函数中还有其他类似的地方:

class B : public C, public A {
  void foo();
}
void bar(A * a) {
  a->foo();
}
它现在需要使用更复杂的分派机制进行编译,因为在编译时我们不知道“a”是指向a还是B的指针

现在来回答问题。假设我们将新的类B添加到我们的代码库中。在我看来,不太可能在使用指向A的指针的任何地方都需要重新编译代码

据我所知,vtables是由编译器创建的。但是,链接器是否可能在重新定位期间解决此修复?在我看来,我很可能就是找不到任何可以确定的证据,因此现在就去睡觉:)

空白条(A*A)
内,指针肯定指向
A
对象。
A
对象可能是其他对象(如
B
)的子对象,但这与此无关。
A
是独立的,有自己的vtable指针,链接到
foo

当发生从
B*
A*
的转换时,例如当用
B*
调用
bar
时,可以向
B*
添加一个常量偏移量,使其指向
A
子对象。如果每个对象中的第一件事是vtable,那么这也将设置指向
A
vtable的指针。对于单个继承,不需要进行调整

下面是一个典型实现的内存:

| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |

B vt: | ptrs to methods of C (with B overrides) | ptrs to methods of B |
AB vt: | ptrs to methods of A (with B overrides) |
(请注意,通常,
AB vt
仍然是
bvt
的一部分;它们在内存中是连续的。
ptr到B的方法
可以在
ptr到A的方法之后。为了格式清晰,我这样写了它。)

当您将
B*
转换为
a*
时,您可以这样做:

| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
^ your B * pointer value
为此:

| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
                                            ^ your A * pointer value

使用从
a*
B*
static\u cast
将指针向后移动到另一个方向。

否,不需要重新编译仅依赖于
a
的代码

这是典型的现代C++编译器坚持的“独立翻译”原则。您可以编写所有只依赖于
A
的代码,这样它就不会包含任何提到
B
的定义。这意味着
B
中的任何更改都不会触发任何
A
特定代码的重新编译。这意味着遵循“独立翻译”原则的C++编译器必须实现简单继承、多重继承、虚拟调度、层次转换等,这样,在<代码> B <代码>中的任何更改-特定代码不需要重新编译任何<代码> A<代码>特定代码。

如果不是这样,典型的C++编译器的体系结构必须有很大的不同。

子类和超类是不正确的C++术语。它们被称为“派生类”和“基类”。唯一需要使用A重新编译代码的是方法声明/接口的更改。所以问题更多的是链接器到底做什么?在链接过程中,我们会犯哪些会导致运行时错误的错误?是否会检测到这些错误,导致中止,或者可能出现某些未定义/不可预测的运行时行为?“继承路径B-C、B-A等可能都有2个vtables。”否。B仍然只有一个vtable,尽管它可能有两个vtable指针。我不明白为什么

AB
表不能在
B
的表中
B
应该只需要一个
vtable
@MooingDuck:当然,通常会有一段内存,包含成员函数指针和类B的类似内容,它的两个VPTR将指向该内存的不同部分。无论是一个包含多个部分的vtable,还是两个碰巧相邻的vtable,都只是一个描述。(好的,通常也只有一个链接器符号。)具体地说,我很好奇为什么你把B的方法的
ptr与C的方法的
ptr放在同一个表中,但与A的方法的
ptr分开。(另外,A的成员不是通常介于C的成员和B的成员之间吗?我一直认为他们是,但我对此一无所知)@MooingDuck只是为了简化并将每个想法放在一行上。阿斯切普勒所说的是大意。
AB
和纯
B
函数可以在
B
vtable的开头之外的任何地方运行,包括在一个完全独立的表中。但它也可能位于同一个链接器对象中。如果
B
引入的对象位于这样一个替代位置,它们将不会在最派生(B)对象开头的指针之后找到,这将是不直观的。谢谢您的回答。实际上,我知道数据布局,但我不知道(更重要的是)多态性是以这种方式实现的。当您将指向B的指针指定给指向A的指针时,指针的值是不同的。事实上,当我想到这一点时,这种行为是意料之中的。