Размещение нового + массива + выравнивание

SomeObj<unsigned int>* Buffer;
char* BufferPtr = MemoryManager::giveMeSomeBytes(resX*resY*sizeof(SomeObj<unsigned int>));
Buffer = new(BufferPtr) SomeObj<unsigned int>[resX*resY];

когда я прохожу мимо этих строк с помощью отладчика, он показывает мне значения для переменных Buffer и BufferPtr:

BufferPtr: 0x0d7f004c
Buffer:    0x0d7f0050

Я не понимаю, почему эти значения различаются. Как я понимаю, размещение new должно использовать память, начиная с адреса "BufferPtr", чтобы инициализировать элементы массива с помощью конструкторов по умолчанию her в выделенной памяти и вернуть указатель на первый байт первого элемента в массиве, который должен быть точно тот же самый байт, который передается новому оператору размещения.

Я понял что-то неправильно или может кто-нибудь сказать мне, почему значения отличаются?

спасибо!

//edit: ok - я исследовал проблему и получил более запутывающие результаты:

    int size = sizeof(matth_ptr<int>);

    char* testPtr1 = (char*)malloc(a_resX*a_resY*sizeof(int));
    int* test1 = new(testPtr1) int[a_resX*a_resY];

    char* testPtr2 = mmgr::requestMemory(a_resX*a_resY*sizeof(int));
    int* test2 = new(testPtr2) int[a_resX*a_resY];

    char* testPtr3 = (char*)malloc(a_resX*a_resY*sizeof(matth_ptr<int>));
    matth_ptr<int>* test3 = new(testPtr3)matth_ptr<int>[a_resX*a_resY];

    char* testPtr4 = mmgr::requestMemory(a_resX*a_resY*sizeof(matth_ptr<int>));
    matth_ptr<int>* test4 = new(testPtr4)matth_ptr<int>[a_resX*a_resY];

отладчик возвращает мне следующие значения для моих переменных:

size: 4

testPtr1:0x05100418
test1:   0x05100418
testPtr2:0x0da80050
test2:   0x0da80050

testPtr3:0x05101458
test3:   0x0510145c
testPtr4:0x0da81050
test4:   0x0da81054

поэтому он явно должен иметь какое-то отношение к моему универсальному классу smartthinter matth_ptr, так что вот оно:

template <class X> class matth_ptr
{
public:
    typedef X element_type;

    matth_ptr(){
        memoryOfst = 0xFFFFFFFF;
    } 

    matth_ptr(X* p) 
    {
        unsigned char idx = mmgr::getCurrentChunkIdx();
        memoryOfst = (int)p-(int)mmgr::getBaseAddress(idx);
        assert(memoryOfst<=0x00FFFFFF || p==0);//NULL pointer is not yet handled
        chunkIdx = idx;
    }
    ~matth_ptr()                {}
    X& operator*()              {return *((X*)(mmgr::getBaseAddress(chunkIdx)+(memoryOfst&0x00FFFFFF)));}
    X* operator->()             {return  ((X*)(mmgr::getBaseAddress(chunkIdx)+(memoryOfst&0x00FFFFFF)));}
    X* get()                    {return  ((X*)(mmgr::getBaseAddress(chunkIdx)+(memoryOfst&0x00FFFFFF)));}


    template<typename T>
    matth_ptr(const matth_ptr<T>& other) {memoryOfst=other.memoryOfst;}//put these two operators into the private part in order to prevent copying of the smartpointers
    template<typename T>
    matth_ptr& operator=(const matth_ptr<T>& other) {memoryOfst = other.memoryOfst; return *this;}
    template<typename T>
    friend class matth_ptr;
private:

    union //4GB adressable in chunks of 16 MB
    {
        struct{
            unsigned char padding[3]; //3 bytes padding
            unsigned char chunkIdx; //8 bit chunk index
        };
        unsigned int memoryOfst; //24bit address ofst
    };

};

может кто-нибудь объяснить мне, что происходит? спасибо!

Ответ 1

Будьте осторожны с размещением новых на массивах. В текущем стандартном взгляде на раздел 5.3.4.12 вы найдете следующее:

new(2,f) T[5] results in a call of operator new[](sizeof(T)*5+y,2,f)

Понятно, что он ожидает, что новый оператор размещения разместит это дополнительное пространство за пределами того, что нужно массиву. "y" указывается только как неотрицательное целочисленное значение. Затем он компенсирует результат новой функции на эту сумму.

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

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

Ответ 2

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

Ответ 3

@Mat, Это действительно большой вопрос. Когда я использовал новое место размещения [], мне не удалось удалить хранилище. Даже если я назову свое собственное симметричное размещение delete [], адрес указателя не совпадает с возвратом моего собственного места размещения []. Это делает размещение new [] совершенно бесполезным, как вы предложили в комментариях.

Единственное решение, которое я нашел, было предложено Джонатаном @: вместо размещения new [], используйте размещение new (non-array) для каждого из элементов массива. Это нормально для меня, поскольку я сам храню размер. Проблема в том, что мне приходится беспокоиться о выравниваниях указателей для элементов, которые новый [] должен делать для меня.

Ответ 4

Как говорили другие, это связано с тем, что ваша реализация на С++ хранит размер массива в начале буфера, который вы передаете новому размещению массива.

Легкое исправление для этого - просто назначить указатель на массив буфера, затем перебрать массив и использовать регулярное (не массив) размещение new для создания каждого объекта в буфере.