派生到基类转换

时间:2010-09-12 08:10:55

标签: c++

如何在内部发生派生类和基类之间的转换,以及编译器如何知道或存储对象的大小?

例如以下内容:

class A
{
public:
 A():x(2){};
private:
 int x;
};

class B : public A
{
public:
 B():A(),y(5){};
private:
 int y;
};

class C : public B
{
public:
 C():B(),z(9){};
private:
 int z;
};

int main()
{
   C *CObj = new C;
   B *pB = static_cast<B*>(CObj);
   delete CObj;
}

编辑:一定是这样的:

B BObj = static_cast<B>(*CObj);

5 个答案:

答案 0 :(得分:6)

您的代码中没有任何“派生到基础”转换。你的代码中有一个指针 - 派生到指针到底部的转换。 (此转换不需要任何显式转换,BTW)

B *pB = CObj; // no need for the cast

为了执行指针转换,不需要知道对象的大小。因此,目前尚不清楚您对“物体大小”的引用来自何处。

实际上,在典型的实现中,非多态类的单继承层次结构的上述转换纯粹是概念性的。即除了简单地将派生指针的数值复制到基指针之外,编译器不会做任何事情。无需额外信息即可执行此操作。没有尺寸,没有任何东西。

在更复杂的情况下(如多重继承),编译器可能确实必须生成调整指针值的代码。它确实需要知道所涉及对象的大小。但涉及的大小总是编译时的,即它们是编译时常量,这意味着编译器会立即知道它们。

在更复杂的情况下,例如虚拟继承,这种转换通常由隐式构建在对象中的运行时结构支持,其中包括所有必要的内容。如果实现选择这样做,也可以包括对象的运行时大小。

答案 1 :(得分:1)

请注意,此处不需要static_cast;将指针派生类“向上转换”为指向父类的指针是完全合法的。

在此示例中,没有转换。指针值保持不变(即在引擎盖下,CObjpB指向相同的内存,尽管多重继承会使事情变得更复杂)。编译器在内存中组织BC个对象的成员,以便一切正常。当我们处理指针时,对象的大小无关紧要(仅在创建new C时才相关)。

如果您有任何虚拟方法,那么我们可以讨论vtable和vptrs(http://en.wikipedia.org/wiki/Vtable)。

答案 2 :(得分:1)

派生类对象具有基类子对象。具体而言,标准在10.3中说明

  

“基类的顺序   子对象分配最多   派生对象(1.8)未指定“

这意味着即使多次,基础子对象可能位于派生对象的开头,也没有必要。因此,从Derived *到Base *的转换是完全未指定的,并且可能留给编译器开发人员一定程度的自由度。

我想说知道语言的规则及其背后的原因很重要,而不是担心编译器如何实现它们。作为一个例子,我看过很多关于VTABLE和VPTR的讨论,这是一个实现动态绑定的编译器特定实现。相反,它有助于了解“独特的最终覆盖”的概念,足以理解虚函数和动态绑定的概念。重点是“什么”而不是“如何”,因为“如何”大部分时间都不是必需的。我说大部分时间都是因为在某些情况下它会有所帮助。一个例子是理解“指向成员的指针”的概念。它有助于知道它通常以某种形式的“偏移”实现,而不是作为常规指针。

答案 3 :(得分:0)

  

如何在内部发生派生类和基类之间的转换

实施定义。
除非您告诉我们您使用的是哪种编译器,否则无法回答 但通常不值得知道或担心(除非你正在编写编译器)。

  

以及编译器如何知道[编辑器]对象的大小

编译器知道大小(它在编译期间计算出C的大小)。

  

还是存储对象的大小?

对象不需要知道大小,因此不会将其存储为类的一部分 运行时内存管理(通过new使用)可能需要知道(但它是实现定义的),以便它可以正确释放内存(但它存储的任何东西都不会在对象中被覆盖)。

答案 4 :(得分:-5)

如果您曾经做过任何C,那么答案就来自于它。

内存分配器根本不关心它存储的内容。它只需知道已分配的内存范围。它没有看到C和int之间的区别[4]。它只需要知道如何释放从给定指针开始的内存范围。