gen*_*sys 26 c++ inheritance alignment
如果使用继承/多重继承,数据成员如何对齐/排序?这个编译器是否具体?
有没有办法在派生类中指定如何对成员(包括基类中的成员)进行排序/对齐?
谢谢!
Bea*_*anz 64
你真的在这里问了很多不同的问题,所以我会尽力按顺序回答每个问题.
首先,您想知道数据成员的对齐方式.成员对齐是编译器定义的,但由于CPU处理错位数据的方式,它们都倾向于遵循相同的方式
结构应该基于最严格的成员(通常但不总是最大的内在类型)对齐,并且结构总是对齐,使得数组的元素全部对齐.
例如:
struct some_object
{
    char c;
    double d;
    int i;
};
这个结构将是24个字节.因为该类包含一个double,所以它将是8字节对齐,这意味着char将被填充7个字节,并且int将被填充4以确保在some_object的数组中,所有元素将是8字节对齐的.一般来说,这是依赖于编译器的,尽管您会发现对于给定的处理器体系结构,大多数编译器将数据对齐相同.
你提到的第二件事是派生类成员.派生类的排序和对齐有点痛苦.类分别遵循我上面描述的结构规则,但是当你开始谈论继承时,你会陷入混乱的草皮.鉴于以下课程:
class base
{
    int i;
};
class derived : public base // same for private inheritance
{
    int k;
};
class derived2 : public derived
{
    int l;
};
class derived3 : public derived, public derived2
{
    int m;
};
class derived4 : public virtual base
{
    int n;
};
class derived5 : public virtual base
{
    int o;
};
class derived6 : public derived4, public derived5
{
    int p;
};
base的内存布局是:
int i // base
派生的内存布局是:
int i // base
int k // derived
derived2的内存布局是:
int i // base
int k // derived
int l // derived2
derived3的内存布局是:
int i // base
int k // derived
int i // base
int k // derived
int l // derived2
int m // derived3
你可能会注意到base和derived在这里出现了两次.这是多重继承的奇迹.
为了解决这个问题,我们有了虚拟继承.
derived4的内存布局是:
base* base_ptr // ptr to base object
int n // derived4
int i // base
derived5的内存布局是:
base* base_ptr // ptr to base object
int o // derived5
int i // base
derived6的内存布局是:
base* base_ptr // ptr to base object
int n // derived4
int o // derived5
int i // base
您将注意到派生的4,5和6都有一个指向基础对象的指针.这是必要的,因此当调用任何base函数时,它有一个传递给这些函数的对象.此结构依赖于编译器,因为它未在语言规范中指定,但几乎所有编译器都实现相同的结构.
当你开始讨论虚函数时,事情会变得更复杂,但同样,大多数编译器也会同样实现它们.参加以下课程:
class vbase
{
    virtual void foo() {};
};
class vbase2
{
    virtual void bar() {};
};
class vderived : public vbase
{
    virtual void bar() {};
    virtual void bar2() {};
};
class vderived2 : public vbase, public vbase2
{
};
这些类中的每一个都包含至少一个虚函数.
vbase的内存布局是:
void* vfptr // vbase
vbase2的内存布局是:
void* vfptr // vbase2
vderived的内存布局是:
void* vfptr // vderived
vderived2的内存布局是:
void* vfptr // vbase
void* vfptr // vbase2
人们对vftables的工作方式有很多不了解的事情.要理解的第一件事是类只存储指向vftables的指针,而不是整个vftables.
这意味着无论一个类有多少虚函数,它都只有一个vftable,除非它通过多重继承从其他地方继承vftable.几乎所有编译器都将vftable指针放在类的其余成员之前.这意味着你可能在vftable指针和类的成员之间有一些填充.
我还可以告诉你,几乎所有编译器都实现了pragma pack功能,允许你手动强制结构对齐.一般来说,除非你真的知道自己在做什么,否则你不想这样做,但它确实在那里,有时它是坏的.
你问的最后一件事是你是否可以控制订购.你总是控制订购.编译器将始终按照您编写的顺序对事物进行排序.我希望这个冗长的解释能够满足您需要知道的所有内容.
| 归档时间: | 
 | 
| 查看次数: | 13790 次 | 
| 最近记录: |