2010-10-18 13 views
14

Что такое вторая строка? (Посещение, отвечая на другой вопрос.)Что это за второе?

int * x = new int [1] ; 
int * y = new (x) int; 

После того, как вторая линия х и у имеют одинаковое значение (точка на одном и том же месте). В чем разница между y = x и второй строкой? Это как конструктор или что-то еще?

+0

Примечание: Размещение не обычно используется с int (поскольку оно не имеет преимущества (int не имеет конструктора)). –

+0

@Martin: Хотя 'new (x) int()' будет отличаться и может использоваться через шаблон. Всегда использовать parens, даже с новым [], является хорошей идеей. – 2010-10-20 14:39:10

+0

Ссылка на другой вопрос, где вы видели это, может быть полезна. – 2010-10-20 14:42:32

ответ

13

Это placement new. Он создает новый int в памяти, на который указывает x.

Если попробовать:

int * x = new int [1]; 
*x = 5; 
std::cout << *x << std::endl; 
int * y = new (x) int; 
*y = 7; 
std::cout << *x << std::endl; 

выход будет:

5 
7 
+0

, но x не меняется там, где он уже указывался! –

+0

Ничего не происходит с 'x'. Но что-то может случиться с '* x'. Попробуйте следующее: 'int * x = new int [1]; * x = 5; std :: cout << * x << std :: endl; int * y = new (x) int; * y = 7; std :: cout << * x << std :: endl; ' –

+0

@ Зеленый код: это правильно. Размещение нового просто выполняет инициализацию. Это как вызов конструктора для этого типа в заданной выделенной памяти. –

4

Это называется placement new. Он позволяет вам создать объект в уже выделенной памяти.

В этой статье обсуждается where and how it is useful for.

+0

Я не совсем понял! когда мы что-то новое снова меняем указатель! но как это работает без изменения указателя! см. вывод VCEXPRESS8: x перед второй строкой 0x005c4e58 x после того, как вторая строка 0x005c4e58 будет одинаковой! –

+1

Он сообщает компилятору «делать обычный конструктор, но вместо того, чтобы создавать больше памяти, используйте память в' x'. » Поэтому он не изменяет 'x', а перезаписывает' int', который вы создали в первой строке. –

+0

@Green, нет новой памяти, выделенной для размещения new, поэтому 'y' в вашем примере указывает на тот же адрес памяти, что и' x'. Подумайте об этом просто как опустить шаг выделения памяти и немедленно выполнить соответствующий вызов конструктора, используя поставляемый блок памяти. –

3

Вторым новым является "размещение нового". Он выполняет инициализацию (т. Е. Вызывает любые необходимые конструкторы) без какого-либо выделения. Это полезно, когда вам нужно создать схему распределения пользовательской памяти.

2
int * y = new (x) int; 

Это как новый синтаксис размещения.

РЕДАКТИРОВАТЬ: Наряду с пользовательским распределением, размещение нового также помогает re-initialze состояние объекта, как показано ниже.

class Test 
{ 
    int startVal; 
public: 
    Test() 
    { 
     startVal = 1; 
    } 
    void setVal(int val) { startVal = val; } 
}; 
int main() 
{ 
    Test *p = new Test; //Creates new object and initializes it with 
          //a call to constructor. 
    p->setVal(10); //Change object content. 
    new(p) Test; //Reset object: 
    //object pointed by p will be re-initialzed here by making 
    //a call to constructor. startVal will be back to 1 
} 

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

+0

Это не очень хорошая идея. Что произойдет, если Test содержит элемент указателя. Поскольку вы не вызывали деструктор на исходном объекте, вы, вероятно, будете утечки памяти. Если вы собираетесь использовать новое место для сброса значения (которое обычно является плохим идеей), вы должны сначала вызвать деструктор на объекте (вы можете вручную вызвать деструктор с помощью указателя). –

+0

@Martin: Хорошая точка. Но я видел, что повторная настройка используется в нашем широко используемом коммерческом коде. У них нет указателя внутри класса. Не уверен, что это плохая практика. В любом случае, спасибо. – bjskishore123

3

Это место размещения новое.

Хотя вы не обычно используете его с целыми типами.
Обычно он используется для создания буфера, в который вы затем встраиваете другие типы.

// Allocate a buffer with enough room for two T objects. 
char* buffer = new char[sizeof(T) * 2]; 

// Allocate a T in slot zero 
T* t1 = new (buffer + 0 * sizeof(T)) T("Zero"); 

// Allocate a T in slot one 
T* t2 = new (buffer + 1 * sizeof(T)) T("One"); 

Thats the basics.
Но помните, что объекты, выделенные с размещением нового , не могут быть удалены delete.. Это связано с тем, что delete пытается восстановить память, выделенную new (а также вызвать деструктор). Поэтому, чтобы правильно использовать эти объекты, вы должны вручную вызвать там деструктор.

t1->~T(); 
t2->~T(); 

Не забудьте удалить исходный буфер.

delete [] buffer; 

Несколько предостережений:
Люди часто видят, что буфер может быть реализован в стек и, таким образом, автоматически освобождается

char buffer[sizeof(T) * 2]; 

К сожалению, это может быть технически ОК (Компилируется). Но не гарантируется работа, так как память буфера может быть не правильно выровнена для размещения Т внутри. Таким образом, вы должны динамически распределять буфер (с помощью новых gurantees, чтобы память была правильно выровнена для любого объекта выделенного размера (таким образом, по расширению он также выравнивается для любого размера, меньшего размера, чем размер, выделенный). Легкий способ обойти эта проблема заключается в использовании зОго :: вектора

std::vector<char> buffer(sizeof(T) * 2); 
T* t1 = new (&buffer[0] + 0 * sizeof(T)) T("Zero"); 
T* t2 = new (&buffer[0] + 1 * sizeof(T)) T("One"); 

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

T obj1("Plop"); 
obj1 = T("Another Plop"); 

// Can be done like this: 
T obj1("Plop"); 
obj1.~T(); 
new (&obj1) T("Another Plop"); // Seems excessive to me. But can be us-full 
           // in some extreme situations. 

Помните, если вы используете метод сброса, вы должны сначала уничтожить старый объект (или объект может не правильно вести себя).