Какие гарантии выравнивания я могу ожидать для массивов в структуре? - PullRequest
1 голос
/ 18 декабря 2009

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

Для этого я «объявляю» их в своем классе следующим образом:

template <class K, class V> class MyClass
{
public:
   MyClass() : wereConstructorsCalled(false) {/* empty */}
   ~MyClass() {if (wereConstructorsCalled) MyCallPlacementDestructorsFunc();}

   [...]

private:
   bool wereConstructorsCalled;
   mutable char keyBuf[sizeof(K)];
   mutable char valBuf[sizeof(V)];
};

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

Читая FAQ по C ++, он сказал, что при использовании нового размещения мне нужно быть осторожным, чтобы размещение было правильно выровнено, иначе у меня возникнут проблемы.

У меня вопрос, будут ли массивы keyBuf и valBuf правильно выровнены во всех случаях, или мне нужно предпринять какой-то дополнительный шаг, чтобы убедиться, что они выровнены правильно? (если так, то не зависящий от платформы шаг был бы предпочтительным)

Ответы [ 7 ]

2 голосов
/ 18 декабря 2009

Могу я спросить, почему вы хотите поместить их в буфер символов? Почему бы просто не создать объекты-указатели K и V, а затем создать их экземпляр, когда вам это нужно.

2 голосов
/ 18 декабря 2009

Нет гарантии, что вы получите соответствующее выравнивание. Обычно для массивов гарантируется выравнивание только для типа элемента. Массив char выровнен для хранения char.

Единственное исключение состоит в том, что массивы char и unsigned char, выделенные с помощью new, имеют максимальное выравнивание, так что вы можете хранить в них произвольные типы. Но эта гарантия не применяется в вашем случае, так как вы избегаете выделения кучи.

TR1 и C ++ 0x добавляют несколько очень полезных типов:

std::alignment_of и std::aligned_storage вместе дают вам портативный (и работающий) ответ.

std::alignment_of<T>::value дает выравнивание, необходимое для типа T. std::aligned_storage<A, S>::type дает вам тип POD с выравниванием A и размером S. Это означает, что вы можете безопасно записать свой объект в переменную типа std::aligned_storage<A, S>::type.

(В TR1 пространство имен - std::tr1, а не просто std)

2 голосов
/ 18 декабря 2009

Может быть, я не понял вашего вопроса, но разве вы не можете просто сделать char *keyBuf[..size..];, изначально установить его в NULL (не выделено) и выделить его в первый раз, когда вам это нужно?

То, что вы пытаетесь сделать с новым размещением, кажется рискованным бизнесом и плохим стилем кодирования.

В любом случае выравнивание кода зависит от реализации.

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

Я рекомендую вам взглянуть на шаблон boost::optional. Он делает то, что вам нужно, даже если вы не можете его использовать, вам, вероятно, стоит взглянуть на его реализацию.

Он использует alignment_of и type_with_alignment для расчетов и гарантий выравнивания.

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

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

Размещение нового отлично подходит для POD (но не спасет вас), но если у вас вообще есть конструктор, он вообще не будет работать.

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

Для размещения новых применений, но не для этого.

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

Я нашел этот ответ, опубликованный SiCrane на http://www.gamedev.net/community/forums/topic.asp?topic_id=455233:

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

Похоже, профсоюз может добиться цели!

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

Если вы хотите изменить выравнивание кода, используйте пакет pragma

#pragma pack(push,x)

// class code here

#pragma pack(pop) // to restore original pack value

если x равен 1, между вашими элементами не будет заполнения.

Вот ссылка для чтения

http://www.cplusplus.com/forum/general/14659/

...