MST
星途 面试题库

面试题:C++ 空类内存布局与多重继承及模板元编程

假设存在空类A、B、C,类D多重继承自A、B、C。请详细分析类D的内存布局,并结合模板元编程的知识,如何在编译期获取类D中每个基类子对象的偏移量?阐述其实现思路和关键技术点。
38.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

类D的内存布局分析

  1. 空类优化:在C++中,空类通常会被优化,占用1字节空间,这是为了保证对象有唯一地址。所以类A、B、C各自在内存中占用1字节。
  2. 多重继承布局:类D多重继承自A、B、C,其内存布局大致是依次排列A、B、C的子对象,具体的顺序由编译器决定,但一般按照继承列表的顺序。假设内存对齐要求为1字节(通常空类情况),类D的大小为3字节,内存布局示意如下:
    • 类D对象内存:
      • [A子对象(1字节)] [B子对象(1字节)] [C子对象(1字节)]

通过模板元编程获取基类子对象偏移量的实现思路

  1. 模板偏特化:利用模板偏特化技术,针对不同的基类类型编写不同的模板特化版本。
  2. offsetof宏offsetof宏可以获取结构体中某个成员的偏移量。我们可以通过将类D看作一个类似结构体的布局,利用offsetof来获取基类子对象偏移量。
  3. 模板元编程递归:通过递归模板实例化来处理多重继承的情况,确保获取到每个基类子对象的偏移量。

关键技术点

  1. 模板偏特化的编写
    template <typename Base, typename Derived>
    struct BaseOffset;
    
    // 针对类A的模板偏特化
    template <typename Derived>
    struct BaseOffset<A, Derived> {
        static const size_t value = offsetof(Derived, A);
    };
    
    // 针对类B的模板偏特化
    template <typename Derived>
    struct BaseOffset<B, Derived> {
        static const size_t value = offsetof(Derived, B);
    };
    
    // 针对类C的模板偏特化
    template <typename Derived>
    struct BaseOffset<C, Derived> {
        static const size_t value = offsetof(Derived, C);
    };
    
  2. 使用方式
    class A {};
    class B {};
    class C {};
    class D : public A, public B, public C {};
    
    int main() {
        size_t offsetA = BaseOffset<A, D>::value;
        size_t offsetB = BaseOffset<B, D>::value;
        size_t offsetC = BaseOffset<C, D>::value;
        return 0;
    }
    
  3. 内存对齐的考虑:实际应用中,需要考虑不同编译器的内存对齐规则。offsetof宏返回的偏移量是基于实际内存对齐后的结果。如果内存对齐要求较高,类D的内存布局可能会出现填充字节,在编写模板元编程获取偏移量时,要确保结果符合实际内存布局。