我正在阅读 Beautiful Code 中的章节在 Linux 内核上,作者讨论了 Linux 内核如何在 C 语言中实现继承(以及其他主题)。简而言之,定义了一个“基础”结构,为了从它继承,“子类”结构将基础的副本放置在子类结构定义的末尾。然后,作者用了几页纸解释了一个巧妙而复杂的宏,以确定要返回多少字节才能从对象的基类部分转换为对象的子类部分。
我的问题:在子类结构中,为什么不将基结构声明为结构中的第一个,而不是最后东西?
将基类结构放在首位的主要优点是,当从基类转换到子类时,您根本不需要移动指针——本质上,进行转换只是意味着告诉编译器让您的代码使用子类结构放置在基类定义的内容之后的“额外”字段。
为了澄清我的问题,让我抛出一些代码:
struct device { // this is the 'base class' struct
int a;
int b;
//etc
}
struct usb_device { // this is the 'subclass' struct
int usb_a;
int usb_b;
struct device dev; // This is what confuses me -
// why put this here, rather than before usb_a?
}
如果碰巧有一个指向 usb_device 对象内部“dev”字段的指针,那么为了将其转换回那个 usb_device 对象,需要从该指针中减去 8。但是,如果“dev”是 usb_device 中的第一件事,则根本不需要移动指针。
如有任何帮助,我们将不胜感激。即使是关于在哪里找到答案的建议也会受到赞赏 - 我真的不确定如何谷歌搜索这样的决定背后的架构原因。我在 StackOverflow 上能找到的最接近的是: why to use these weird nesting structure
而且,要明确一点——我知道很多聪明的人已经在 Linux 内核上工作了很长时间,所以很明显,这样做是有充分理由的,我只是想不通它是什么.
最佳答案
Amiga OS 在很多地方都使用了这种“公共(public) header ”技巧,这在当时看起来是个好主意:通过简单地转换指针类型来进行子类化。但也有缺点。
临:
- 您可以扩展现有的数据结构
- 您可以在需要基类型的所有地方使用相同的指针,不需要指针运算,节省宝贵的周期
- 感觉很自然
缺点:
- 不同的编译器倾向于以不同的方式对齐数据结构。如果基本结构以
char a;
结尾,那么在子类的下一个字段开始之前,您可以在之后有 0、1 或 3 个填充字节。这导致了非常严重的错误,尤其是当你必须保持向后兼容性时(即出于某种原因,你必须有一定的填充,因为一个古老的编译器版本有一个错误,现在,有很多代码需要错误填充) . - 传递错误的结构时,您不会很快注意到。使用您问题中的代码,如果指针算法错误,字段很快就会被丢弃。这是一件好事,因为它增加了更早发现错误的机会。
- 它导致一种态度“我的编译器会为我修复它”(有时它不会)并且所有的转换导致“我比编译器更了解”的态度。后者会让您在理解错误消息之前自动插入转换,这会导致各种奇怪的问题。
Linux 内核将通用结构放在别处;它可以但不必在最后。
临:
- 错误会提前显示
- 你必须对每个结构做一些指针运算,所以你已经习惯了
- 你不需要强制转换
缺点:
- 不明显
- 代码比较复杂
关于c - Linux 内核 : why do 'subclass' structs put base class info at end?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28040069/