当前位置: 首页 > 知识库问答 >
问题:

C带有“空类”的多继承内存布局

潘修为
2023-03-14

我知道多重继承的内存布局没有定义,所以我不应该依赖它。但是,在特殊情况下,我可以依赖它吗?也就是说,一个类只有一个“真正的”超级类。所有其他类都是“空类”,即既没有字段也没有虚拟方法的类(即它们只有非虚拟方法)。在这种情况下,这些附加类不应该在类的内存布局中添加任何内容(更简洁地说,在C 11的措辞中,该类具有标准布局)

我能推断出所有的超类都没有偏移吗?例如。:

#include <iostream>

class X{

    int a;
    int b;
};

class I{};

class J{};

class Y : public I, public X,  public J{};

int main(){

    Y* y = new Y();
    X* x = y;
    I* i = y;
    J* j = y;

    std::cout << sizeof(Y) << std::endl 
                  << y << std::endl 
                  << x << std::endl 
                  << i << std::endl 
                  << j << std::endl;
}

在这里,Y 是类,其中 X 是唯一真正的基类。程序的输出(在带有g 4.6的linux上编译时)如下:

8个。

0x233f010号

0x233f010号

0x233f010号

0x233f010号

正如我所总结的,没有指针调整。但是这个实现是特定的还是我可以依赖它。也就是说,如果我收到一个I类型的对象(我知道只有这些类存在),我可以使用一个reinterpret_cast将其转换为X吗?

我希望我可以依赖它,因为规范规定对象的大小必须至少是一个字节。因此,编译器无法选择其他布局。如果它将IJ布局在

我是对的,还是我在这里使用从IX reinterpret_cast,我是在玩火?

共有1个答案

常甫
2023-03-14

在C 11中,编译器需要对标准布局类型使用空基类优化。参见https://stackoverflow.com/a/10789707/981959

对于您的具体示例,所有类型都是标准布局类,没有通用的基类或成员(见下文),因此您可以依赖C 11中的行为(实际上,我认为许多编译器已经遵循了该规则,当然G也遵循了该规则,而其他编译器则遵循了Itanium C ABI。)

警告:确保你没有任何相同类型的基类,因为它们必须在不同的地址,例如

struct I {};

struct J : I {};
struct K : I { };

struct X { int i; };

struct Y : J, K, X { };

#include <iostream>

Y y;

int main()
{
  std::cout << &y << ' ' << &y.i << ' ' << (X*)&y << ' ' << (I*)(J*)&y << ' ' << (I*)(K*)&y << '\n';

}

印刷品:

0x600d60 0x600d60 0x600d60 0x600d60 0x600d61

对于类型 Y,只有一个 I 基可以处于偏移零,因此尽管 X 子对象处于偏移零(即 offsetof(Y, i) 为零)并且其中一个 I 基位于同一地址,但另一个 I 基是(至少用 G 和 Clang)进入对象的一个字节, 因此,如果您获得I *,则无法reinterpret_cast X*,因为您不知道它指向哪个I子对象,即偏移量为0的I或偏移量为1的I

编译器可以将第二个I子对象放在偏移量1处(即在int内部),因为I没有非静态数据成员,因此您实际上不能取消引用或访问该地址的任何内容,只能获得指向该地址对象的指针。如果您将非静态数据成员添加到I,那么Y将不再是标准布局,也不必使用EBO,并且offsetof(Y, i)将不再为零。

 类似资料:
  • 主要内容:多继承下的构造函数,命名冲突在前面的例子中,派生类都只有一个基类,称为 单继承(Single Inheritance)。除此之外, C++也支持 多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。 多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、 C#、 PHP 等干脆取消了多继承。 多继承的语法也很简单,将多个基类用逗号隔开即可。例如已声明了类A

  • 在面向对象的程序设计中,定义一个新的 class 的时候,可以从某个现有的 class 继承,新的 class 称为子类,而被继承的 class 称为基类、父类或超类。 Python 中继承的语法如下: class Parent: pass class Child(Parent): pass 在第 1 行,定义了父类 Parent; 在第 4 行,定义了子类 Child,语法

  • 本文向大家介绍关于C++对象继承中的内存布局示例详解,包括了关于C++对象继承中的内存布局示例详解的使用技巧和注意事项,需要的朋友参考一下 前言 本文给大家介绍的是关于C++对象继承的内存布局的相关内容,分享出来供大家参考学习,在开始之前说明下,关于单继承和多继承的简单概念可参考此文章 以下编译环境均为:WIN32+VS2015 虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Fun

  • 我有一个类,用于使用struct解码二进制数据并存储在NamedTuple中,如下所示: 这是没有问题的,我可以如下使用: 但是,如果我尝试将其更改为继承类方法,如下所示,它会失败: 然后它错误与。 我知道从NamedTuple继承有些问题,但我想知道是否有解决办法? 编辑:正如下面的其他人所暗示的那样,看起来数据类是要走的路:一种用于类型检查目的的子类命名Tuple的方法

  • 本文向大家介绍C/C++ 公有继承、保护继承和私有继承的对比详解,包括了C/C++ 公有继承、保护继承和私有继承的对比详解的使用技巧和注意事项,需要的朋友参考一下 C/C++ 公有继承、保护继承和私有继承的区别    在c++的继承控制中,有三种不同的控制权限,分别是public、protected和private。定义派生类时,若不显示加上这三个关键字,就会使用默认的方式,用struct定义的类

  • 问题内容: 我正在开发一个使用angular作为客户端框架的应用程序,目前使用angular作为岩石,我真的很高兴,尽管现在我发现我经常复制和粘贴要组织成类层次结构的代码。例如,对话框共享一组通用的功能,需要打开,关闭它们,提供功能的代码也是从某些父BaseTypeaheadClass继承的首选对象,尽管我在angular中没有发现的一件事是组织这些层次结构。控制器,服务和提供者都在下面使用普通的