размещение-новый адрес против необработанного адреса памяти

Результат размещения new всегда кажется таким же, как указатель памяти, который я предоставляю размещению new. С GCC это, похоже, верно даже для классов с виртуальными функциями, например ...

#include <iostream>
#include <vector>

using namespace std;

class A
{
public:
    int a;
    virtual ~A() {}
};

int main()
{
    void *mem = malloc(sizeof(A));
    A* ptr = new(mem) A();

    cout << "sizeof(T) = " << sizeof(A) << endl;
    cout << "mem = " << mem << endl;
    cout << "ptr = " << ptr << endl;
    cout << "addr a = " << &(ptr->a) << endl;

    ptr->~A();
    free(mem);

    return 0;
}

Вывод этой программы (примечание: 64-битный Linux) ...

sizeof(T) = 16
mem = 0x1a41010
ptr = 0x1a41010
addr a = 0x1a41018

Гарантирует ли C ++, что mem и ptr идентичны, или это просто совпадение GCC? В более крупной переносимой программе мне нужно будет сохранять и mem, и ptr, или я могу просто сохранить один из них и использовать при необходимости?

Чтобы немного прояснить вопрос, я знаю, что распределители памяти иногда помещают размер выделенного блока в слово перед указанным блоком памяти. Разрешено ли компиляторам C ++ использовать подобные уловки и сказать, что указатель VMT помещается в слово перед блоком памяти, на который указывает указатель объекта? В этом случае mem и ptr будут разными.


person goertzenator    schedule 11.03.2012    source источник


Ответы (2)


Да, они такие же. Вы можете думать об адресе памяти как о пустом указателе, а об адресе объекта как о типизированном указателе, но, если хотите, вы можете выполнить приведение. В некотором смысле new - это способ «преобразовать» адрес памяти в объект (путем его создания). Вот полная картина (обратите внимание на отсутствие приведения):

void * addr = std::malloc(sizeof(T));  // or ::operator new(sizeof(T))
T * p = ::new (addr) T;                // "new" gives you an object pointer
p->~T();
std::free(addr);

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

Возможно, вы захотите взглянуть на свою реализацию std::allocator, чтобы увидеть новое размещение и приведение в действие.

person Kerrek SB    schedule 11.03.2012

Вы используете void* operator new (std::size_t size, void* ptr) throw(); версию оператора new. Вот что cplusplus.com говорит об этом операторе:

Это версия размещения, которая не выделяет память - она ​​просто возвращает ptr. Обратите внимание, что конструктор объекта (если он есть) по-прежнему будет вызываться выражением оператора.

Это означает, что mem и ptr всегда будут одинаковыми. Я думаю, что хранить их обоих - плохая идея, и лучше использовать void* operator new (std::size_t size) throw (std::bad_alloc); версию оператора new, например. в вашем случае: A* ptr = new A;;

person Artak Begnazaryan    schedule 11.03.2012
comment
Это хороший ответ, но насколько он авторитетен? Основан ли он на стандарте или на реализации конкретного компилятора? - person goertzenator; 12.03.2012
comment
@goertzenator В стандарте C ++ определены 3 версии оператора new. Тот, который я вам предложил, наиболее часто используется в программах на C ++. Полную документацию и примеры их использования см. здесь. - person Artak Begnazaryan; 12.03.2012