POD和继承在C ++ 11。第一个成员的struct地址是否为地址? [英] PODs and inheritance in C++11. Does the address of the struct == address of the first member?

查看:123
本文介绍了POD和继承在C ++ 11。第一个成员的struct地址是否为地址?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(我已编辑这个问题,以避免分心。有一个核心问题需要在任何其他问题有意义之前清除)对任何人的答案现在似乎不太相关。)



让我们设置一个特定的例子:

  struct Base {
int i ;
};

没有虚方法,没有继承,通常是一个非常蠢的简单对象。因此,它是普通旧数据(POD),它回到了可预测的布局。特别是:

  Base b; 
& b == reinterpret_cast< B *>&(b.i);

这是根据维基百科(其本身声称引用C ++ 03标准):


指向POD结构体对象的指针,使用重新解释转换适当地转换,指向它的初始成员,反之亦然,这意味着在POD结构的开头没有填充。[8]


现在让我们考虑继承:

  struct派生:public Base {
};

同样,没有虚拟方法,没有虚拟继承,没有多重继承。因此,这是POD。



问题:这个事实(Derived是C ++ 11中的POD)允许我们说:

 派生d; 
& d == reinterpret_cast< D *>&(d.i); // true on g ++ - 4.6



如果这是真的,那么以下将被明确定义:

  Base * b = reinterpret_cast< Base *>(malloc(sizeof(Derived))); 
free(b); //它将释放相同的地址,所以这是确定

code> new 和 delete 这里 - 更容易考虑 malloc code> free 。我只是想知道在简单的情况下派生对象的布局的规定,并且基类的初始非静态成员在可预测的位置。



是一个派生对象应该等效于:

  struct Derived {//没有继承
Base b ; //它只是包含它
};



你不在乎POD,你关心标准布局。以下是标准部分9 [class] 的定义:


-layout类是一个类:




  • 没有非标准布局类型的非静态数据成员类型)或引用,

  • 没有虚拟函数(10.3)和没有虚拟基类(10.1),

  • 具有相同的访问控制11)对于所有非静态数据成员,

  • 没有非标准布局基类,

  • 没有非静态数据成员在大多数派生类中,并且最多一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且

  • 没有相同类型的基类作为第一个非静态数据成员。


9.2 [class.mem] ):


指向标准布局struct对象,使用 reinterpret_cast 适当地转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。


这实际上比旧的要求更好,因为 reinterpret_cast






现在让我们移动到你的第二个问题。答案不是你想要的。

  Base * b = new Derived; 
delete b;

是未定义的行为,除非 Base 析构函数。请参阅第5.3.5节( [expr.delete]


替代(删除对象),如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类,并且静态类型应具有虚拟析构函数或者行为未定义。







您的早期代码片段使用 malloc free 大多是正确的。这将工作:

  Base * b = new(malloc(sizeof(Derived)))Derived; 
free(b);

,因为指针 b 与从展示位置new返回的地址相同,后者又是从 malloc 返回的相同地址。


(I've edited this question to avoid distractions. There is one core question which would need to be cleared up before any other question would make sense. Apologies to anybody whose answer now seems less relevant.)

Let's set up a specific example:

struct Base {
    int i;
};

There are no virtual method, and there is no inheritance, and is generally a very dumb and simple object. Hence it's Plain Old Data (POD) and it falls back on a predictable layout. In particular:

Base b;
&b == reinterpret_cast<B*>&(b.i);

This is according to Wikipedia (which itself claims to reference the C++03 standard):

A pointer to a POD-struct object, suitably converted using a reinterpret cast, points to its initial member and vice versa, implying that there is no padding at the beginning of a POD-struct.[8]

Now let's consider inheritance:

struct Derived : public Base {
};

Again, there are no virtual methods, no virtual inheritance, and no multiple inheritance. Therefore this is POD also.

Question: Does this fact (Derived is POD in C++11) allow us to say that:

Derived d;
&d == reinterpret_cast<D*>&(d.i); // true on g++-4.6

If this is true, then the following would be well-defined:

Base *b = reinterpret_cast<Base*>(malloc(sizeof(Derived)));
free(b); // It will be freeing the same address, so this is OK

I'm not asking about new and delete here - it's easier to consider malloc and free. I'm just curious about the regulations about the layout of derived objects in simple cases like this, and where the initial non-static member of the base class is in a predictable location.

Is a Derived object supposed to be equivalent to:

struct Derived { // no inheritance
    Base b; // it just contains it instead
};

with no padding beforehand?

解决方案

You don't care about POD-ness, you care about standard-layout. Here's the definition, from the standard section 9 [class]:

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

And the property you want is then guaranteed (section 9.2 [class.mem]):

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

This is actually better than the old requirement, because the ability to reinterpret_cast isn't lost by adding non-trivial constructors and/or destructor.


Now let's move to your second question. The answer is not what you were hoping for.

Base *b = new Derived;
delete b;

is undefined behavior unless Base has a virtual destructor. See section 5.3.5 ([expr.delete])

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.


Your earlier snippet using malloc and free is mostly correct. This will work:

Base *b = new (malloc(sizeof(Derived))) Derived;
free(b);

because the value of pointer b is the same as the address returned from placement new, which is in turn the same address returned from malloc.

这篇关于POD和继承在C ++ 11。第一个成员的struct地址是否为地址?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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