2016-10-09 2 views
1
#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 

Есть ли раздел в стандарте C++ 14, который требует «нового» для создания определенного макета? Есть ли гарантия, что макет, созданный новым, помещается в буфер размера sizeof (B) и со смещением нуля?Как новое место размещения определяет, какой макет следует создавать?


Редактировать: Не могли бы вы использовать grep-friendly терминологию или предоставить ссылки? Я добавил ссылку на стандарт на вопрос.

Примите во внимание образец, показанный выше: Что означает номер 24? Каков размер буфера?

В стандарте может быть утверждение, что наиболее производный объект всегда является прямой, непрерывной копией представления объекта, , но я не нашел этого.

Вещь, которую мы знаем о новой, заключается в том, что она должна использоваться с полным типом объекта. [expr.new]

Существует пример для нового выражения с опцией размещения в [class.dtor] §12.4 (14). Однако пример может работать просто потому, что класс в нем является стандартным макетом.

ответ

2

Где гарантия, что макет, созданный новые приступы в буфер размера SizeOf (B) и со смещением нулевой

От типа был назван в new в качестве аргумента является B. A B делается, а не D. Тип B «ничего не знает» о D. Объявление D не влияет на B; декларация B может быть переведена в единицы перевода, в которых D не появляется, но везде в программе будет достигнуто соглашение о размере B и является макетом, независимо от того, известен ли в этих местах D.

Объект C++ типа T имеет размер sizeof T. Это означает, что он вписывается в sizeof T байтов; это не может быть так, что для его представления требуется (sizeof T) + k байт, где k > 0.

+0

Не должно быть 'k! = 0'? –

+0

@AlexisWilke Если 'k' является' signed int' и отрицательным, что происходит, когда он добавляется в 'size_t'? :) – Kaz

+0

Я не уверен ... Вы говорите мне. Но я думаю, что, наверное, поэтому я говорю, что это должно быть 'k! = 0', а не' k> 0'. Вы написали ответ ... –

1

Невозможно создать то, что вы называете «отдельным» макетом, кроме создания производного типа, и вывести из него B.

«Макет B как часть его производного класса» - это не то же самое, что «Макет B». Место размещения new и обычный new используют макет, основанный на самом типе, с стандартным автономным расположением.

Где гарантия того, что макет, созданный новым, вписывается в буфер размера sizeof(B)?

sizeof(B) возвращает размер B самих, а не B ей часть-из-некоторых-другого класса.Это все пространство, необходимое для хранения автономного B, независимо от того, как вы выделяете для него память.

0

Есть две вещей, которые происходят в новом выражении:

  1. Выделение памяти для типа с помощью вызова оператора нового (который не является нового выражения)

  2. Строительства нужного типа.

Обычно оператор new может использовать размер, который передается компилятором внутри нового выражения. Имея место размещения нового (предлагая хранилище), программист несет ответственность за обеспечение хранения, достаточно большого для хранения требуемого типа.

В любом случае: не смешивайте распределение и объект строительства. Это две разные задачи.

0

Просто точность: способ реализации виртуального наследования состоит в том, чтобы нажимать (все) виртуальные базовые классы в конце классов после всех объявлений полей собственных членов. Здесь должна быть организация класса D и класса В.

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

Это не new, который выбирает макет для void* rawObject= new (storage) B;; Это факт, что вы создаете конкретный объект B. Таким образом, макет такой же, как у contiguous - второй.

Смежные вопросы