How does placement new know which layout to create?

时光总嘲笑我的痴心妄想 提交于 2019-12-20 04:07:11

问题


#include <iostream>
#include <typeinfo>
struct A { int a; };
struct B : virtual A { int b; };
struct C : virtual A  { int c; };
struct D : B,C { int d; };
int main()
{
  D complete;
  B contiguous;
  B & separate = complete;
  B * p[2] = {&separate, &contiguous};
  // two possible layouts for B: 
  std::cout<< (int)((char*)(void*) &p[0]->a -(char*)(void*)&p[0]->b)<<" "<< sizeof(*p[0])<< "\n";
  std::cout<< (int)((char*)(void*) &p[1]->a -(char*)(void*)&p[1]->b)<<" "<< sizeof(*p[1])<< "\n";

  alignas(B) char buff[sizeof(B)];
  void * storage = static_cast<void*>(buff);
  // new expression skips allocation function:
  auto pointer= new (storage) B;        // Which layout to create?
  std::cout << typeid(pointer).name()<<"\n"; 
  pointer->~B();    // Destructor knows layout through typed pointer.
}
// sample output (Debian 8, amd64):
// 24 16
// 4 16
// P1B

Is there a section in the C++14 standard that reqires 'new' to create a particular layout? Is there guarantee that the layout created by new fits into a buffer of size sizeof(B) and with offset zero?


edit: Could you please use grep-friendly terminology or provide references? I added a reference to the standard to the question.

Take into consideration the sample output above: What does the number 24 tell you? What is the size of the buffer?

There might be a statement in the standard that a most derived object is always a straightforward, contiguous copy of the object representation, but I haven't found this one.

The thing we know about new is that it shall be used with a complete object type. [expr.new]

There is an example for a new-expression with the placement option in [class.dtor] §12.4 (14). However, the example might work simply because the class therein is standard-layout.


回答1:


Where is the guarantee that the layout created by new fits into a buffer of size sizeof(B) and with offset zero

From the the type being named in new as its argument being B. A B is being made, not an D. The type B "knows nothing" about D. The declaration of D has no influence on B; the B declaration can be put into translation units in which D doesn't appear, yet everywhere in the program there will be agreement about the size of B and is layout, regardless of whether D is also known in those places or not.

A C++ object of type T has a size sizeof T. This means that it fits into sizeof T bytes; it cannot be the case that (sizeof T) + k bytes are required for its representation, where k > 0.




回答2:


There is no option to create what you call a "separate" layout, other than creating a derived type, and fishing B out of it.

"Layout of B as portion of its derived class" is not the same as "Layout of B". Placement new and regular new use the layout based on the type itself, with is the default, stand-alone layout.

Where is the guarantee that the layout created by new fits into a buffer of size sizeof(B)?

sizeof(B) returns the size of B itself, not B-as-part-of-some-other-class. That is all the space needed to store a stand-alone B, regardless of the way that you allocate memory for it.




回答3:


Just a precision: a way to implement virtual inheritance is to push (all) the virtual base classes at the end of the classes after all the own member fields declarations. Here should be the organization of class D and class B.

class D:
   -----
     pvmt for B, D (include shift to find A)
     int b;
   -----
     pvmt for C (include shift to find A)
     int c;
   -----
     int a;
   -----

class B:
   -----
     pvmt for B (include shift to find A)
     int b;
   -----
     int a;
   -----

class C:
   -----
     pvmt for C (include shift to find A)
     int c;
   -----
     int a;
   -----

It is not new that chooses the layout for void* rawObject= new (storage) B;; It is the fact that you create a concrete B object. So the layout is the same than the one for contiguous - the second one.




回答4:


There are two things happening in a new expression:

  1. Allocating memory for a type by calling operator new (which is no new expression)

  2. Construction of the desired type.

Usually the operator new can utilize the size, which is passed by the compiler inside the new expression. Having a placement new (offering storage), the programmer is responsible to provide a storage large enough to hold the desired type.

Anyway: Do not mix allocation and object construction. These are two distinct tasks.



来源:https://stackoverflow.com/questions/39946780/how-does-placement-new-know-which-layout-to-create

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!