2009-12-16 1 views
1

Так у меня есть несколько структур ...Как переопределить память с помощью new для выделения переменных внутри структуры?

struct myBaseStruct 
{ 
}; 

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

myDerivedStruct* pNewStruct; 

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

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

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

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

Спасибо,

+0

Хорошо, спасибо за обратную связь. Меня соблазняет идея malloc, но я не знаком с парадигмами, которые предполагали бы, что это плохая идея? Во что бы то ни стало назовите меня n00b - альтернативно, некоторые ссылки на документацию, детализирующие это, будут гораздо более продуктивными. – acron

+0

Если вам нужна непрерывная структура переменной длины, вам нужно положить массив в конец, а не указатель. –

+0

Спасибо. Да, на основании ответа Ремуса, это то, что я сделал, и в сочетании с использованием malloc я получил его на работу. Чтобы пояснить, причина, по которой построена структура, заключается в том, что я могу сохранить указатель неспецифического типа, а затем просто memcpy в цикле. Процесс фактически выполняет следующие действия: - Считывает бит типа блока. - Считывает размер блока. - Mallocs - переменная-член правильного типа с расширенным размером. - Использует указатель локального базового класса, указывающий на переменную-член. - Размер Memcpy в указатель. Это работает на петле, основанной на незнании того, что следует за блоком. – acron

ответ

1

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

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

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

struct myStruct 
{ 
size_t size;  
int a, b, c, d;  
char ident[0]; 
}; 
+1

Объявление массива размера 0 плохо сформировано в C++ (а также в C, BTW). * У всех компиляторов есть проблемы с этим, а не только с некоторыми. – AnT

+0

Да, поэтому, исходя из предположения, что каждая строка будет иметь как минимум 1 символ, я использовал 'char ident;'. Этот метод сработал. – acron

+0

'char ident;'? В чем смысл? Как вы собираетесь получить доступ к строке? Применяйте '' '' '' 'каждый раз? Это будет работать, но в чем смысл? – AnT

1

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

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

однако

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

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

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

+2

О, Боже, пожалуйста, прекрати боль! –

+0

Почему это плохая идея? – acron

+0

Потому что: - Вы всегда должны * делать все распределение памяти для этих структур самостоятельно, иначе вы в конечном итоге повредите память. - Значения полей вашей структуры, когда вы memcpy в нее будут зависимыми от платформы (наиболее очевидно, что они будут зависеть от endianness и размера int). - sizeof (myDerivedStruct) вводит в заблуждение - Ваш код будет очень запутанным для чтения, и любой, кто его поддерживает в будущем, может не понимать, что вы делаете, даже если вы это делаете. – James

1

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

+0

Я думаю, что это, наверное, лучшая идея. "D'о." – acron

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

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

+0

Я хотел бы отказаться от того, что делать это так Плохая идея (tm) Если вы используете C++, вы должны использовать std :: string – rossipedia

+0

STL не считается оптимальным для платформы, в которой я работаю, к сожалению:/ – acron

+0

.... хотя это делает то же самое, оператор new, str/memcpy – paulm

4

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

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

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

+0

Сверху моей головы я не думаю, что это правда. У него нет виртуальных функций или настраиваемого конструктора по умолчанию, поэтому я думаю, что это POD. – James

+1

Сверху моей головы, 9/4: «POD-struct - это совокупный класс» и 8.5.1/1: «Агрегат - это массив или класс с ... без базовых классов». Хорошо, я солгал, что это не в моей голове ;-) –

+0

Хорошо, Spec выигрывает: D – James

6

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

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

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

myDerivedClass * Foo = новый myDerivedClass (а, Ь, с, d, идентификаторы);

+0

Я мог бы сделать это, но он эффективно разрушает эффективность. Кроме того, входящий буфер имеет хорошо упакованные данные - какова точка в расщеплении всего этого, только чтобы собрать его? Все, что я хочу сделать, это скопировать данные из буфера и дать ему некоторый контекст, используя мою структуру. – acron

+0

@acron: спросите себя, насколько важна эффективность * настолько, что вы не можете позволить себе писать надежный, читаемый код на C++. Если вы определите, что это так, рассмотрите возможность использования C, как было предложено. –

0

Является ли размер буфера известен во время компиляции? В этом случае статически назначенный массив будет более простым решением. В противном случае см. Ответ Ремуса Русану выше. Вот как win32 api управляет структурами с переменным размером.

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

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

Во-вторых, то, что вы указали в своем оригинальном посте, не будет работать с описанным вами макетом данных. За то, что вы описали в ОП, необходимо последний член структуры, чтобы быть массивом, а не указатель

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, предполагая, что размер правильный.

3

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

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 
}; 
Смежные вопросы