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

派生类中基类的大小

施晗昱
2023-03-14

假设我有一个没有数据的类:

struct Empty {
  /*some methods here*/
};

和派生类

struct Derived: Empty {
  int a;
  int b;
  char c;
  ....
}__attribute__((packed));`

Empty类的对象的大小为1。派生类的空部分的大小通常为0。据我所知,编译器看到基Empty类没有数据,因此它可以优化Empty的大小,以防它“在”Derived中,但标准并不要求这样做。

所以问题是:

我能在编译时确定Derived类的Empty部分并没有占用内存吗。

我知道我可以像大小(派生)=大小(a)大小(b)一样进行检查...但它太冗长了,并且有几个类,如派生。有没有更优雅的解决方案

共有2个答案

滕夜洛
2023-03-14

您可以使用更多“旧”(C 11之前)宏-offsetof:

struct Empty {};
struct NonEmpty {
  int a;
};
struct Derived1: Empty {
  int a;
  int b;
  char c;
};
struct Derived2: NonEmpty {
  int a;
  int b;
  char c;
};
static_assert(offsetof(Derived1,a) == 0,"");
static_assert(offsetof(Derived2,a) != 0,"");

您也可以使用此宏检查成员变量的顺序:

static_assert(offsetof(Derived,a) < offsetof(Derived,b),"");
static_assert(offsetof(Derived,b) < offsetof(Derived,c),"");

但是不要忘记- offsetof也有同样的限制:

如果type不是标准布局类型,则行为未定义。如果成员是静态成员或成员函数,则行为未定义。

慕河
2023-03-14

您可以使用 std::is_empty 来确保从中继承的类的大小为零:

static_assert(std::is_empty<Empty>{});

如果是,则保证对标准布局类进行空基优化。

我知道我可以像sizeof(Derived)=sizeof(a)sizeof(b)…但是它太冗长了。有更优雅的解决方案吗?

这无法正常工作,因为您需要考虑填充和最终属性,如packed

 类似资料:
  • 一个类的对象经常会是另一个类的对象。例如,矩形当然是四边形(正方形、平行四边形和梯形也是这样),因此可以说矩形类 Rectangle 是从四边形类 Quadrilateral 继承而来的。在本例中,类 Quadrilateral 叫做基类,类 Rectangle 称为派生类。矩形是四边形的一种特殊类型,但是要说四边形是矩形则是不正确的。图 9.1 示例了几个简单的继承例子。 基类 派生类 stud

  • 下面是一个人为的例子:(实际代码中使用的命名方案太令人困惑) 我有一个班主任,他是第三方图书馆的一部分,不可更改。我也有一些课程延伸到父亲;比如儿子。祖父有一个类似这样的构造函数: 这个构造函数中实际发生的事情并不重要。重要的是,它所做的任何事情都必须由所有派生类完成,因此对的调用是每个派生类构造函数的第一行。这里的含义是,祖父的所有后代的构造函数,无论距离有多远,都必须始终调用super(或调用

  • 从已有的对象类型出发建立一种新的对象类型,使它部分或全部继承原对象的特点和功能,这是面向对象设计方法中的基本特性之一。继承不仅简化了程序设计方法,显著提高了软件的重用性,而且还使得软件更加容易维护。派生则是继承的直接产物,它通过继承已有的一个或多个类来产生一个新的类,通过派生可以创建一种类族。   继承 基本概念 在定义一个类A时,若它使用了一个已定义类B的部分或全部成员,则称类A继承了类B,并称

  • 我有一个应用程序,它由一个服务器组成,该服务器可以有多个两种类型的客户端,即用户客户端和设备客户端。为此,我有一个客户端基类的向量,我将在其中添加新客户端,因为它们连接到服务器。这是我所拥有的简化版本: 我有下面的UML表示: 我怀疑这张图是否代表了正确的意图。也许我对UML不是很有经验,这就足够了,但是我认为这个图没有清楚地描述服务器中的向量将包含用户和设备而不是客户端的事实。我不知道我是否需要

  • 和构造函数类似,析构函数也不能被继承。与构造函数不同的是,在派生类的析构函数中不用显式地调用基类的析构函数,因为每个类只有一个析构函数,编译器知道如何选择,无需程序员干涉。 另外析构函数的执行顺序和构造函数的执行顺序也刚好相反: 创建派生类对象时,构造函数的执行顺序和继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。 而销毁派生类对象时,析构函数的执行顺序和继承顺序相反,即先执行派生类析构

  • 主要内容:构造函数的调用顺序,基类构造函数调用规则前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数, 类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。 在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法