问题
#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:
Allocating memory for a type by calling operator new (which is no new expression)
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