массив символов как хранилище для размещения новых - PullRequest
8 голосов
/ 03 января 2011

Является ли следующий допустимый C ++ с четко определенным поведением?

class my_class { ... };

int main()
{
    char storage[sizeof(my_class)];
    new ((void *)storage) my_class();
}

Или это проблематично из-за соображений приведения / выравнивания указателя?

Ответы [ 5 ]

13 голосов
/ 03 января 2011

Да, это проблематично.У вас просто нет гарантии, что память выровнена должным образом.

Хотя существуют различные приемы для получения хранилища с правильным выравниванием, лучше всего использовать Boost или C ++ 0x aligned_storage, которые скрывают эти приемы отyou.

Тогда вам просто нужно:

// C++0x
typedef std::aligned_storage<sizeof(my_class),
                                alignof(my_class)>::type storage_type;

// Boost
typedef boost::aligned_storage<sizeof(my_class),
                        boost::alignment_of<my_class>::value>::type storage_type;

storage_type storage; // properly aligned
new (&storage) my_class(); // okay

Обратите внимание, что в C ++ 0x, используя атрибуты, вы можете просто сделать это:

char storage [[align(my_class)]] [sizeof(my_class)];
3 голосов
/ 03 января 2011

Как уже упоминали люди, это не обязательно будет работать из-за ограничений выравнивания. Есть несколько способов получить правильное выравнивание. Во-первых, если у вас есть C ++ 0x-совместимый компилятор, вы можете использовать оператор alignof, чтобы попытаться заставить выравнивание быть корректным. Во-вторых, вы можете динамически распределять массив символов, так как память от оператора new гарантированно будет выровнена таким образом, что любой сможет использовать его правильно. В-третьих, вы можете попытаться сохранить массив символов в объединении с типом, который имеет максимально возможное выравнивание в вашей системе; Я считаю, что эта статья содержит некоторую информацию (хотя она разработана для C ++ 03 и, конечно, не так хороша, как оператор alignof, который скоро выйдет).

Надеюсь, это поможет!

2 голосов
/ 29 июля 2012

Если кто-то хочет избежать Boost или C ++ 1x, этот полный код работает как в GCC, так и в MSVC.Специфичный для MSVC код основан на align_memory.h Chromium.Это немного сложнее, чем версия GCC, потому что MSVC __declspec(align(.)) принимает только буквальные значения выравнивания, и это обходится с использованием специализации шаблона для всех возможных выравниваний.

#ifdef _MSC_VER

template <size_t Size, size_t Align>
struct AlignedMemory;

#define DECLARE_ONE_ALIGNED_MEMORY(alignment) \
template <size_t Size> \
struct __declspec(align(alignment)) AlignedMemory<Size, alignment> { \
    char mem[Size]; \
};

DECLARE_ONE_ALIGNED_MEMORY(1)
DECLARE_ONE_ALIGNED_MEMORY(2)
DECLARE_ONE_ALIGNED_MEMORY(4)
DECLARE_ONE_ALIGNED_MEMORY(8)
DECLARE_ONE_ALIGNED_MEMORY(16)
DECLARE_ONE_ALIGNED_MEMORY(32)
DECLARE_ONE_ALIGNED_MEMORY(64)
DECLARE_ONE_ALIGNED_MEMORY(128)
DECLARE_ONE_ALIGNED_MEMORY(256)
DECLARE_ONE_ALIGNED_MEMORY(512)
DECLARE_ONE_ALIGNED_MEMORY(1024)
DECLARE_ONE_ALIGNED_MEMORY(2048)
DECLARE_ONE_ALIGNED_MEMORY(4096)

#else

template <size_t Size, size_t Align>
struct AlignedMemory {
    char mem[Size];
} __attribute__((aligned(Align)));

#endif

template <class T>
struct AlignedMemoryFor : public AlignedMemory<sizeof(T), __alignof(T)> {};
2 голосов
/ 03 января 2011

Это как минимум проблематично из-за выравнивания.

В большинстве архитектур не-Intel код генерирует «ошибку шины» из-за неправильного выравнивания или слишком медленный из-за перехватов процессора, необходимых для исправления невыровненного доступа к памяти.

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

0 голосов
/ 03 января 2011

Массив char может быть неправильно выровнен для размера myclass.На некоторых архитектурах это означает более медленный доступ, а на других это означает сбой.Вместо char вы должны использовать тип, выравнивание которого равно или больше, чем у struct, что задается наибольшим требованием выравнивания среди всех его членов.выделите достаточно памяти для одного my_class экземпляра, я думаю size должно быть sizeof(my_class) / sizeof(T), где T - это тот тип, который вы используете для получения правильного выравнивания.

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