C ++数据对齐/成员为了与放大器;遗产 [英] c++ data alignment /member order & inheritance

查看:205
本文介绍了C ++数据对齐/成员为了与放大器;遗产的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何数据成员获得对准/如果继承/多重继承用于订购?这是编译器的具体点吗?

有没有一种方法来指定在派生类中的成员(包括从基类成员)如何责令/对齐?

谢谢!

解决方案

真是你问了很多不同的问题在这里,所以我会尽我所能回答每一个反过来。

首先,你要知道数据成员对齐。会员对准定义的编译器,但由于CPU的如何处理未对齐的数据,他们都倾向于遵循同样的

准则该结构应根据最严格的部件(通常是,但不总是,最大本征型),和strucutres总是对准,使得数组的元素全部对准同一上对齐。

例如:

 结构some_object
{
    焦炭℃;
    双D;
    INT I;
};
 

此结构将是24个字节。因为类包含双这将是8字节对齐,这意味着焦炭将由7字节来填充,和整型将由4被填充,以确保在some_object的阵列,所有的元素将是8字节对齐的。一般来说,这是编译器相关的,但你会发现,对于给定的处理器架构,大多数编译器对齐数据相同。

你提到的第二件事是派生类中的成员。订购和派生类的定位是有点痛苦。类分别按照我上述的结构规则,但是当你开始谈论继承你进入凌乱的草坪。鉴于以下类:

 阶级基础
{
    INT I;
};

类派生的:公共基础//相同的私有继承
{
    INT K表;
};

类Derived2的:公派生
{
    INT升;
};

类derived3:公派生,公共Derived2的
{
    INT米;
};

类derived4:公共虚拟基
{
    INT N;
};

类derived5:公共虚拟基
{
    INT O;
};

类derived6:公共derived4,公共derived5
{
    INT磷;
};
 

有关基地的内存布局将是:

  INT I //基地
 

对于得到的内存布局将是:

  INT I //基地
时int k //派生
 

有关Derived2的内存布局将是:

  INT I //基地
时int k //派生
int类型l // Derived2的
 

有关derived3的内存布局将是:

  INT I //基地
时int k //派生
INT I //基地
时int k //派生
int类型l // Derived2的
INT米// derived3
 

您可能会注意到,基类和派生每个出现在这里两次。这是多重继承的奇迹。

要解决这个问题,我们有虚拟继承。

有关derived4的内存布局将是:

 基础* base_ptr // PTR到基本对象
INTñ// derived4
INT I //基地
 

有关derived5的内存布局将是:

 基础* base_ptr // PTR到基本对象
INT O // derived5
INT I //基地
 

有关derived6的内存布局将是:

 基础* base_ptr // PTR到基本对象
INTñ// derived4
INT O // derived5
INT I //基地
 

您会注意到导出4,5,6都有一个指向基对象。这是necissary以便调用任何碱的功能时,它的目的是通过对这些功能。这种结构是编译器相关的,因为它不是在语言规范中指定,但几乎所有的编译器执行该相同的

让事情当你开始谈论虚函数较为复杂,但同样,大多数编译器实现它们同样也是如此。看看下面的类:

 类VBASE
{
    虚拟无效美孚(){};
};

类vbase2
{
    虚拟无效栏(){};
};

类vderived:公共VBASE
{
    虚拟无效栏(){};
    虚拟无效BAR2(){};
};

类vderived2:公共VBASE,公共vbase2
{
};
 

每个这些类的含有至少一个虚拟函数

有关VBASE的内存布局将是:

 无效*的vfptr // VBASE
 

有关vbase2的内存布局将是:

 无效*的vfptr // vbase2
 

有关vderived的内存布局将是:

 无效*的vfptr // vderived
 

有关vderived2的内存布局将是:

 无效*的vfptr // VBASE
无效*的vfptr // vbase2
 

有很多东西的人不明白如何vftables工作。要了解的第一件事是,类指针只存储vftables,而不是整个vftables。

这也就意味着,无论有多少虚函数的类了,只会有一个vftable,除非它继承了从别的地方通过多重继承一个vftable。 pretty的多所有的编译器把vftable指针类的其他成员的面前。这意味着,你可能有vftable指针和类成员之间的一些填充。

我还可以告诉你,几乎所有的编译器执行编译包功能,让你可以手动强制结构调整。一般来说,你不想这样做,除非你真的知道你在做什么,但它是存在的,有时是necissary。

的最后一件事,你问的是,如果你能控制顺序。你总是控制顺序。你把它们写在编译器总是订购的东西的顺序,我希望这长篇大论的解释打你,你需要知道的一切。

How do data members get aligned / ordered if inheritance / multiple inheritance is used? Is this compiler specific?

Is there a way to specify in a derived class how the members (including the members from the base class) shall be ordered / aligned?

Thanks!

解决方案

Really you’re asking a lot of different questions here, so I’m going to do my best to answer each one in turn.

First you want to know how data members are aligned. Member alignment is compiler defined, but because of how CPUs deal with misaligned data, they all tend to follow the same

guideline that structures should be aligned based on the most restrictive member (which is usually, but not always, the largest intrinsic type), and strucutres are always aligned such that elements of an array are all aligned the same.

For example:

struct some_object
{
    char c;
    double d;
    int i;
};

This struct would be 24 bytes. Because the class contains a double it will be 8 byte aligned, meaning the char will be padded by 7 bytes, and the int will be padded by 4 to ensure that in an array of some_object, all elements would be 8 byte aligned. Generally speaking this is compiler dependant, although you will find that for a given processor architecture, most compilers align data the same.

The second thing you mention is derived class members. Ordering and alignment of derived classes is kinda a pain. Classes individually follow the rules I described above for structs, but when you start talking about inheritance you get into messy turf. Given the following classes:

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;
};

The memory layout for base would be:

int i // base

The memory layout for derived would be:

int i // base
int k // derived

The memory layout for derived2 would be:

int i // base
int k // derived
int l // derived2

The memory layout for derived3 would be:

int i // base
int k // derived
int i // base
int k // derived
int l // derived2
int m // derived3

You may note that base and derived each appear twice here. That is the wonder of multiple inheritance.

To get around that we have virtual inheritance.

The memory layout for derived4 would be:

base* base_ptr // ptr to base object
int n // derived4
int i // base

The memory layout for derived5 would be:

base* base_ptr // ptr to base object
int o // derived5
int i // base

The memory layout for derived6 would be:

base* base_ptr // ptr to base object
int n // derived4
int o // derived5
int i // base

You will note that derived 4, 5, and 6 all have a pointer to the base object. This is necissary so that when calling any of base's functions it has an object to pass to those functions. This structure is compiler dependant because it isn't specified in the language spec, but almost all compilers implement it the same.

Things get more complicated when you start talking about virtual functions, but again, most compilers implement them the same as well. Take the following classes:

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
{
};

Each of these classes contains at least one virtual function.

The memory layout for vbase would be:

void* vfptr // vbase

The memory layout for vbase2 would be:

void* vfptr // vbase2

The memory layout for vderived would be:

void* vfptr // vderived

The memory layout for vderived2 would be:

void* vfptr // vbase
void* vfptr // vbase2

There are a lot of things people don't understand about how vftables work. The first thing to understand is that classes only store pointers to vftables, not whole vftables.

What that means is that no matter how many virtual functions a class has, it will only have one vftable, unless it inherits a vftable from somewhere else via multiple inheritance. Pretty much all compilers put the vftable pointer before the rest of the members of the class. That means that you may have some padding between the vftable pointer and the class's members.

I can also tell you that almost all compilers implement the pragma pack capabilities which allow you to manually force structure alignment. Generally you don't want to do that unless you really know what you are doing, but it is there, and sometimes it is necissary.

The last thing you asked is if you can control ordering. You always control ordering. The compiler will always order things in the order you write them in. I hope this long-winded explanation hits everything you need to know.

这篇关于C ++数据对齐/成员为了与放大器;遗产的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆