Как перераспределить память, используя new для выделения переменных внутри структуры? - PullRequest
1 голос
/ 17 декабря 2009

Итак, у меня есть пара структур ...

struct myBaseStruct
{
};

struct myDerivedStruct : public myBaseStruct
{
    int a, b, c, d;
    unsigned char* ident;
};

myDerivedStruct* pNewStruct;

... и я хочу динамически выделить достаточно места, чтобы я мог «memcpy» в некоторых данных, включая строку с нулевым символом в конце. Размер базовой структуры, по-видимому, равен 1 (я полагаю, что он не может быть нулевым), а размер производного равен 20, что, кажется, имеет смысл (5 x 4).

Итак, у меня есть буфер данных размером 29, первые 16 байтов - это целые, а оставшиеся 13 - это строка.

Как я могу выделить достаточно памяти для pNewStruct, чтобы было достаточно для строки? В идеале я просто хочу пойти:

  • выделить 29 байтов в pNewStruct;
  • memcpy из буфера в pNewStruct;

Спасибо

Ответы [ 9 ]

6 голосов
/ 17 декабря 2009

Вы возвращаетесь к C или отказываетесь от этих идей и фактически используете C ++, как и предполагалось.

  • Используйте конструктор для выделения памяти и деструктор для ее удаления.
  • Не позволяйте другому коду записывать в ваше пространство памяти, создайте функцию, которая будет гарантировать выделение памяти.
  • Используйте std: string или std :: vector для хранения данных, а не для прокрутки своего собственного класса контейнера.

В идеале вы должны просто сказать:

myDerivedClass * foo = new myDerivedClass (a, b, c, d, идент);

4 голосов
/ 17 декабря 2009

В текущем стандарте C ++ myDerivedStruct не является POD, поскольку имеет базовый класс. Результат memcpy ввода чего-либо в него не определен.

Я слышал, что C ++ 0x ослабит правила, так что POD больше классов, чем в C ++ 98, но я не рассматривал это. Кроме того, я сомневаюсь, что очень многие компиляторы будут размещать ваш класс таким образом, который несовместим с POD. Я ожидаю, что у вас будут проблемы только с тем, что не выполняет пустую оптимизацию базового класса. Но это так.

Если это был POD, или если вы готовы рисковать своей реализацией, то вы могли бы использовать malloc(sizeof(myStruct)+13) или new char[sizeof(myStruct)+13], чтобы выделить достаточно места, в основном так же, как в C. Предположительно, мотивация состоит в том, чтобы избежать затрат памяти и времени на простое добавление члена std::string в ваш класс, но за счет необходимости написания кода для ручного управления памятью.

3 голосов
/ 17 декабря 2009

Вы можете перераспределить для любого экземпляра класса, но это подразумевает определенную нагрузку на управление. Единственный действительный способ сделать это - использовать пользовательский вызов выделения памяти. Без изменения определения класса вы можете сделать это.

void* pMem = ::operator new(sizeof(myDerivedStruct) + n);
myDerivedStruct* pObject = new (pMem) myDerivedStruct;

Если вы не перегружаете operator delete в иерархии, тогда delete pObject будет правильным способом уничтожить pObject и освободить выделенную память. Конечно, если вы выделяете какие-либо объекты в области избыточной памяти, вы должны правильно освободить их, прежде чем освободить память.

Затем у вас есть доступ к n байтов необработанной памяти по этому адресу: void* p = pObject + 1. Вы можете memcpy данные в и из этой области, как вам нравится. Вы можете назначить сам объект и не должны memcpy его данные.

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

myDerivedStruct* pObject = new (n) myDerivedStruct;

и

struct myDerivedStruct
{
    // ...
    void* operator new(std::size_t objsize, std::size_t excess storage);

    // other operator new and delete overrides to make sure that you have no memory leaks
};
1 голос
/ 17 декабря 2009

Вы можете выделить любой размер с помощью malloc :

myDerivedStruct* pNewStruct = (myDerivedStruct*) malloc(
      sizeof(myDerivedStruct) + sizeof_extra data);

Однако у вас есть другая проблема, в которой myDerivedStruct :: ident является очень неоднозначной конструкцией. Это указатель на символ (массив), тогда структуры заканчиваются адресом , где начинается массив символов? Ident может указывать куда угодно и очень неоднозначно, кому принадлежит массив, на который указывает Ident. Мне кажется, что вы ожидаете, что структура оканчивается самим массивом char, а структура владеет дополнительным массивом. Такие структуры обычно имеют размерный элемент для отслеживания собственного размера, чтобы функции API могли правильно управлять ими и копировать их, и дополнительные данные запускаются, как правило, после завершения структуры. Или они заканчиваются массивом длины 0 char ident[0], хотя это создает проблемы с некоторыми компиляторами. По многим причинам в таких структурах нет места наследованию:

struct myStruct 
{
size_t size;    
int a, b, c, d;    
char ident[0];
};
1 голос
/ 17 декабря 2009

Смешивание memcpy и new кажется ужасной идеей в этом контексте. Попробуйте вместо этого использовать malloc.

1 голос
/ 17 декабря 2009

Вы можете динамически распределять пространство, выполняя:

myDerivedStruct* pNewStruct = reinterpret_cast<myDerivedStruct*>(new char[size]);

однако

Вы уверены, что хотите это сделать?

Кроме того, обратите внимание, что если вы намереваетесь использовать идентификатор в качестве указателя на начало вашей строки, это будет неправильно. Вы указываете на необходимость и идентификатор, так как переменная идентификатора сама находится в начале вашего неиспользуемого пространства, интерпретируя то, что в , что пространство как указатель, скорее всего, будет бессмысленным. Следовательно, было бы более разумно, если бы они были unsigned char или char, а не unsigned char*.

[редактировать снова] Я просто хотел бы подчеркнуть, что то, что вы делаете, действительно очень плохая идея.

0 голосов
/ 17 декабря 2009

Во-первых, я не понимаю, какой смысл иметь myBaseStruct базу. Вы не предоставили никаких объяснений.

Во-вторых, то, что вы заявили в исходном сообщении, не будет работать с макетом данных, который вы описали. Для того, что вы описали в OP, вам нужно, чтобы последний член структуры был массивом, а не указателем

struct myDerivedStruct : public myBaseStruct {
    int a, b, c, d;
    unsigned char ident[1];
};

Размер массива не имеет значения, но он должен быть больше 0. Массивы размера 0 явно недопустимы в C ++.

В-третьих, если вы по какой-то причине хотите использовать конкретно new, вам придется выделить буфер из char объектов необходимого размера, а затем преобразовать результирующий указатель в тип указателя

char *raw_buffer = new char[29];
myDerivedStruct* pNewStruct = reinterpret_cast<myDerivedStruct*>(raw_buffer);

После этого вы можете сделать memcpy, предполагая, что размер правильный.

0 голосов
/ 17 декабря 2009

Известен ли размер буфера во время компиляции? Статически размещенный массив будет более простым решением в этом случае. В противном случае см. Ответ Ремуса Русану выше. Вот как Win32 API управляет структурами переменного размера.

struct myDerivedStruct : public myBaseStruct
{
    int a, b, c, d;
    unsigned char ident[BUFFER_SIZE];
};
0 голосов
/ 17 декабря 2009
char* buffer = [some data here];
myDerivedStruct* pNewStruct = new myDerivedStruct();
memcpy(buffer,pNewStruct,4*sizeof(int));
pNewStruct->ident = new char[ strlen(buffer+(4*sizeof int)) ];
strcpy(pNewStruct->ident,buffer+(4*sizeof int));

Что-то в этом роде.

...