2013-11-26 6 views
6

Является ли следующий действующий C++? Это альтернативный способ реализации хвоста переменной длины для плоской структуры. В C это обычно делается с struct hackЯвляется ли это альтернативой C++ для взлома структуры?

struct Str 
{ 
    Str(int c) : count(c) {} 
    size_t count; 
    Elem* data() { return (Elem*)(this + 1); } 
}; 

Str* str = (Str*)new char[sizeof(Str) + sizeof(Elem) * count]; 
new (str) Str(count); 
for (int i = 0; i < count; ++i) 
    new (str->data() + i) Elem(); 
str->data()[0] = elem0; 
str->data()[1] = elem1; 
// etc... 

я прошу это в ответ на следующий related question

+1

Я думаю, что это не будет работать, если 'Elem' имеет различное выравнивание, чем' Str'. – Henrik

+1

Это легко исправлено: 'union {size_t count, Elem dummy; } ' – MSalters

+0

Если вы хотите быть действительно фантастическим, используйте метапрограммирование шаблонов, чтобы сделать' dummy' меньшим из 'Elem' и' std :: max_align_t'. – MSalters

ответ

2

Нет, это не действует:

Элем может иметь различное выравнивание, чем Str, так (reinterpret_) casting Str+1 до Elem* может или не может дать вам действительный указатель, и acccessing может дать неопределенное поведение.

Но ведь почему вы бы хотели сделать что-то подобное?

+0

Почему? Низкий уровень хакерства. Спасибо, спасибо. – john

+0

Обычная причина - местность ссылки. См. Также 'std :: make_shared'. Кроме того, IIRC 'new char []' соответствующим образом выровнен для всех типов (чтобы он мог функционировать как замена для 'malloc') – MSalters

+0

@MSalters:' new char [] 'соответствующим образом выровнен, но после смещения' Str 'все ставки отключены. –

0

Действительно в каком смысле? Это C++, используя методы C-like, которые imho прекрасны, пока требования к проекту не оставляют другого выбора. Если вы спрашиваете, будет ли он работать, то до тех пор, пока проблемы с выравниванием данных не приведут к краху кода (т. Е. Не x86, например, SPARC и т. Д.). C++ ведет себя так же, как C при обращении к памяти.

Я проверил его, используя следующие модификации под GCC и VS, и она работает:

struct Elem 
{ 
Elem() : x(0), t(0) { memset(c, 0, sizeof(c));} 
Elem(int v) : x(v), t(0) { memset(c, 0, sizeof(c));} 
Elem(const Elem &e) { *this = e; } 

Elem &operator=(const Elem &e) 
{ 
    if (this != &e) 
    { 
     memcpy(c, e.c, sizeof(c)); 
     x = e.x; 
     t = e.t; 
    } 
    return *this; 
} 

char c[21]; 
int x; 
char t; 
}; 

struct Str 
{ 
    Str(int c) : count(c) {} 
    size_t count; 
    Elem* data() { return (Elem*)(this + 1); } 
}; 

int count = 11; 

Str *str = (Str*)new char[sizeof(Str) + sizeof(Elem) * count]; 
new (str) Str(count); 
for (int i = 0; i < count; ++i) 
{ 
    new (str->data() + i) Elem(); 
    str->data()[i] = Elem(i+1); 
} 

for (int i=0; i<str->count; i++) 
    cout << "[" << i << "]: " << str->data()[i].x << endl; 

Кроме того, я добавил различные различные элементы размера, чтобы Str и Elem, чтобы заставить другую прокладку и играл с выравниваниями (VS/некоторое GCC: #pragma pack (...), GCC: __ attribute__ ((aligned (...))) и, __ атрибут __ (упакован)).

Пожалуйста, обратите внимание, что игра с выравниваниями не является безопасным на все архитектуры - Relevant question

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