面试题答案
一键面试一、对象内存布局的影响因素
-
虚基类:
- 虚基类会导致对象内存布局的变化。当存在虚基类时,为了实现虚基类子对象的共享,编译器会引入额外的指针或偏移量信息。例如,在多重继承中,如果有多个派生类继承自同一个虚基类,这些派生类对象中对于虚基类子对象只会有一份实例,通过特定的机制来访问。
- 编译器会在对象中添加一个指向虚基类子对象的指针(或者偏移量信息),这个指针通常被称为虚基类指针(vbp)。这会增加对象的大小,并影响对象内存布局中成员变量的相对位置。
-
普通继承关系:
- 对于普通继承,派生类对象会包含基类的子对象。基类子对象会按照继承顺序依次放置在派生类对象的内存空间中。例如,若有
class A
,class B : public A
,class C : public B
,那么C
对象的内存布局中,A
子对象在最前面,接着是B
子对象(不包括A
部分,因为A
已包含在前面),最后是C
自己的成员变量。 - 基类的访问权限(
public
、protected
、private
)不会直接影响对象内存布局,但会影响派生类对基类成员的访问方式。
- 对于普通继承,派生类对象会包含基类的子对象。基类子对象会按照继承顺序依次放置在派生类对象的内存空间中。例如,若有
-
虚函数:
- 如果类中包含虚函数,会有一个虚函数表指针(vptr)。这个指针指向一个虚函数表(vtbl),虚函数表中存储了虚函数的地址。虚函数表指针通常位于对象内存布局的起始位置(在某些编译器实现下)。当有多个虚函数时,虚函数表会按照声明顺序存储虚函数地址。这不仅会增加对象的大小(通常为一个指针的大小,如在 64 位系统中为 8 字节),还会影响对象内存布局。
二、通过代码获取对象内存布局相关信息的示例
#include <iostream>
#include <cstdint>
class Base {
public:
virtual void virtualFunction() {}
int baseData;
};
class Derived : public Base {
public:
int derivedData;
};
class VirtualBase {
public:
virtual void virtualFunction() {}
int virtualBaseData;
};
class DerivedWithVirtualBase : virtual public VirtualBase {
public:
int derivedWithVirtualBaseData;
};
int main() {
std::cout << "Size of Base: " << sizeof(Base) << " bytes" << std::endl;
std::cout << "Offset of baseData in Base: " << offsetof(Base, baseData) << " bytes" << std::endl;
std::cout << "Size of Derived: " << sizeof(Derived) << " bytes" << std::endl;
std::cout << "Offset of derivedData in Derived: " << offsetof(Derived, derivedData) << " bytes" << std::endl;
std::cout << "Size of VirtualBase: " << sizeof(VirtualBase) << " bytes" << std::endl;
std::cout << "Offset of virtualBaseData in VirtualBase: " << offsetof(VirtualBase, virtualBaseData) << " bytes" << std::endl;
std::cout << "Size of DerivedWithVirtualBase: " << sizeof(DerivedWithVirtualBase) << " bytes" << std::endl;
std::cout << "Offset of derivedWithVirtualBaseData in DerivedWithVirtualBase: " << offsetof(DerivedWithVirtualBase, derivedWithVirtualBaseData) << " bytes" << std::endl;
return 0;
}
sizeof
操作符:用于获取对象的大小。通过sizeof(Base)
、sizeof(Derived)
等可以了解不同类对象的总体大小,从侧面反映内存布局对对象大小的影响。offsetof
宏:定义在<cstdint>
头文件中,用于获取结构体或类中某个成员的偏移量。例如,offsetof(Base, baseData)
可以获取baseData
在Base
类对象中的偏移量,帮助分析对象内存布局中成员变量的位置关系。
通过上述代码和分析,可以对存在复杂继承层次结构(包括虚基类和普通继承关系)的对象内存布局有更深入的理解和探究。