问题描述
有以下代码:
#include <iostream>
class Base
{
public:
Base() {
std::cout << "Base: " << this << std::endl;
}
int x;
int y;
int z;
};
class Derived : Base
{
public:
Derived() {
std::cout << "Derived: " << this << std::endl;
}
void fun(){}
};
int main() {
Derived d;
return 0;
}
输出:
Base: 0xbfdb81d4
Derived: 0xbfdb81d4
但是当Derived类中的函数'fun'更改为virtual时:
However when function 'fun' is changed to virtual in Derived class:
virtual void fun(){} // changed in Derived
然后两个构造函数中'this'的地址不同:
Then address of 'this' is not the same in both constructors:
Base: 0xbf93d6a4
Derived: 0xbf93d6a0
另一件事是,如果类Base是多态的,例如我添加了一些其他虚函数:
The other thing is if class Base is polymorphic, for example I added there some other virtual function:
virtual void funOther(){} // added to Base
然后再次发送'this'匹配的地址:
then addresses of both 'this' match again:
Base: 0xbfcceda0
Derived: 0xbfcceda0
问题是 - 当Base类不是多态并且Derived类是?
The question is - why 'this' address is different in Base and Derived class when Base class is not polymorphic and Derived class is?
推荐答案
当你有一个类的多态单继承层次结构时,大多数(如果不是全部)编译器遵循的典型约定是那个中的每个对象层次结构必须以VMT指针(指向虚方法表的指针)开始。在这种情况下,VMT指针很早就被引入到对象内存布局中:通过多态层次结构的根类,而所有下层类只是继承它并将其设置为指向它们正确的VMT。在这种情况下,任何派生对象中的所有嵌套子对象都具有相同的此
值。这样,通过读取 * this
的内存位置,无论实际的子对象类型如何,编译器都可以立即访问VMT指针。这正是您上次实验中发生的情况。当您使根类具有多态性时,所有此
值都匹配。
When you have a polymorphic single-inheritance hierarchy of classes, the typical convention followed by most (if not all) compilers is that each object in that hierarchy has to begin with a VMT pointer (a pointer to Virtual Method Table). In such case the VMT pointer is introduced into the object memory layout early: by the root class of the polymorphic hierarchy, while all lower classes simply inherit it and set it to point to their proper VMT. In such case all nested subobjects within any derived object have the same this
value. That way by reading a memory location at *this
the compiler has immediate access to VMT pointer regardless of the actual subobject type. This is exactly what happens in your last experiment. When you make the root class polymorphic, all this
values match.
但是,当层次结构中的基类时不是多态的,它不会引入VMT指针。 VMT指针将由层次结构中较低位置的第一个多态类引入。在这种情况下,流行的实现方法是在层次结构的非多态(上层)部分引入的数据之前插入VMT指针。这是您在第二个实验中看到的内容。 派生
的内存布局如下所示
However, when the base class in the hierarchy is not polymorphic, it does not introduce a VMT pointer. The VMT pointer will be introduced by the very first polymorphic class somewhere lower in the hierarchy. In such case a popular implementational approach is to insert the VMT pointer before the data introduced by the non-polymorphic (upper) part of the hierarchy. This is what you see in your second experiment. The memory layout for Derived
looks as follows
+------------------------------------+ <---- `this` value for `Derived` and below
| VMT pointer introduced by Derived |
+------------------------------------+ <---- `this` value for `Base` and above
| Base data |
+------------------------------------+
| Derived data |
+------------------------------------+
同时,层次结构的非多态(上)部分中的所有类都不应该知道任何VMT指针。 Base
类型的对象必须以数据字段 Base :: x
开头。同时,层次结构的多态(较低)部分中的所有类必须以VMT指针开头。为了满足这两个要求,编译器被迫调整对象指针值,因为它在层次结构中从一个嵌套的基础子对象上下转换为另一个。这立即意味着跨多态/非多态边界的指针转换不再是概念性的:编译器必须添加或减去一些偏移量。
Meanwhile, all classes in the non-polymorphic (upper) part of the hierarchy should know nothing about any VMT pointers. Objects of Base
type must begin with data field Base::x
. At the same time all classes in the polymorphic (lower) part of the hierarchy must begin with VMT pointer. In order to satisfy both of these requirements, the compiler is forced to adjust the object pointer value as it is converted up and down the hierarchy from one nested base subobject to another. That immediately means that pointer conversion across the polymorphic/non-polymorphic boundary is no longer conceptual: the compiler has to add or subtract some offset.
来自非多态的子对象层次结构的一部分将共享它们的此
值,而来自层次结构的多态部分的子对象将共享它们自己的,不同的此
价值。
The subobjects from non-polymorphic part of the hierarchy will share their this
value, while subobjects from the polymorphic part of hierarchy will share their own, different this
value.
在沿层次结构转换指针值时,必须添加或减去一些偏移量并不罕见:编译器在处理多继承层次结构时必须始终执行此操作。但是,您的示例显示了如何在单继承层次结构中实现它。
Having to add or subtract some offset when converting pointer values along the hierarchy is not unusual: the compiler has to do it all the time when dealing with multiple-inheritance hierarchies. However, you example shows how it can be achieved in single-inheritance hierarchy as well.
加法/减法效果也将在指针转换中显示
The addition/subtraction effect will also be revealed in a pointer conversion
Derived *pd = new Derived;
Base *pb = pd;
// Numerical values of `pb` and `pd` are different if `Base` is non-polymorphic
// and `Derived` is polymorphic
Derived *pd2 = static_cast<Derived *>(pb);
// Numerical values of `pd` and `pd2` are the same
这篇关于当基类不是多态而是派生时,'this'地址不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!