Что это за вторая новинка?

Что такое вторая линия? (Видел, отвечая на другой вопрос.)

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

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


person Amir Zadeh    schedule 18.10.2010    source источник
comment
Примечание. Размещение new обычно не используется с int (поскольку у него нет преимуществ (у int нет конструктора)).   -  person Martin York    schedule 18.10.2010
comment
@Martin: Хотя new(x) int() будет другим, и его можно будет использовать через шаблон. Всегда использовать круглые скобки, даже с new[], — хорошая идея.   -  person    schedule 20.10.2010
comment
Ссылка на другой вопрос, где вы это видели, может быть полезна.   -  person    schedule 20.10.2010


Ответы (5)


Это новое место размещения. Он создает новый 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
person Oliver Charlesworth    schedule 18.10.2010
comment
С 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; - person Oliver Charlesworth; 18.10.2010
comment
@Зеленый код: это правильно. Размещение new просто выполняет инициализацию. Это похоже на вызов конструктора для этого типа в данной выделенной памяти. - person Matteo Italia; 18.10.2010
comment
в чем разница между y=x и y= new (x) int? то, что вы сделали, также может быть сделано с помощью y=x; *у=7; - person Amir Zadeh; 18.10.2010
comment
@Green Code: для примитивных типов да, по сути нет никакой разницы. Для типов классов есть большая разница, так как конструктор будет вызываться снова. - person Oliver Charlesworth; 18.10.2010
comment
Это то, что я искал;), также вы проголосовали за один из моих вопросов, где я имел в виду родителя в запутанности или базовом классе. Я отредактировал, чтобы вы могли отменить голосование: D. Танкс; - person Amir Zadeh; 18.10.2010
comment
@Geen Code: новое размещение обычно не используется таким образом. Увидеть ниже. - person Martin York; 18.10.2010
comment
Помните new(x) int != new(x) int()! - person ; 20.10.2010
comment
@Martin: Что это внизу? :) - person ; 20.10.2010

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

В этом более раннем потоке обсуждается где и как это полезно.

person Péter Török    schedule 18.10.2010
comment
Я не совсем понимаю! когда мы что-то новое снова, его указатель изменяется! но как это работает без изменения указателя! см. вывод VCEXPRESS8: x перед второй строкой 0x005c4e58 x после второй строки 0x005c4e58 одинаковы! - person Amir Zadeh; 18.10.2010
comment
Он сообщает компилятору, что он выполняет обычный конструктор, но вместо создания дополнительной памяти использует память по адресу x. Таким образом, он не изменяет x, а перезаписывает int, созданный вами в первой строке. - person Jonathan Grynspan; 18.10.2010
comment
@Green, новая память не выделяется с новым размещением, поэтому y в вашем примере указывает на тот же адрес памяти, что и x. Думайте об этом как о простом пропуске шага выделения памяти и немедленном выполнении соответствующего вызова конструктора, используя предоставленный блок памяти. - person Péter Török; 18.10.2010
comment
Итак, если я правильно понял, то в итоге вы получите 2 указателя на структуру в одном блоке памяти? Зачем тогда использовать X и Y? Почему бы просто не придерживаться X? - person EKI; 18.10.2010
comment
@EKI, в приведенном выше примере используется тип int, где нет большой разницы. Однако с непримитивными типами может быть большая разница. Вы можете, например. выделите массив из char подходящего размера, затем инициализируйте в нем объект YourFavouriteType с новым размещением. Затем у вас есть char* и YourFavouriteType*, указывающие на физически одну и ту же память, но видимые, возможно, очень разные вещи. - person Péter Török; 18.10.2010
comment
@EKI, более того, вы можете инициализировать несколько отдельных объектов по последовательным адресам в одном и том же большом буфере памяти - это в основном то, что может делать пользовательский распределитель. - person Péter Török; 18.10.2010

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

person Ferruccio    schedule 18.10.2010

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

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

// 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");

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

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

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

delete [] buffer;

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

char buffer[sizeof(T) * 2];

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

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.

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

person Martin York    schedule 18.10.2010

int * y = new (x) int; 

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

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

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
}

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

person bjskishore123    schedule 18.10.2010
comment
Это не очень хорошая идея. Что произойдет, если Test содержит член-указатель. Поскольку вы не вызвали деструктор исходного объекта, вероятно, произойдет утечка памяти. Если вы собираетесь использовать новое размещение для сброса значения (что обычно является плохой идеей), вы должны сначала вызвать деструктор объекта (вы можете вручную вызвать деструктор с помощью указателя). - person Martin York; 18.10.2010
comment
@Мартин: Хороший вопрос. Но я видел, как переустановка используется в нашем широко используемом коммерческом коде. У них нет указателя внутри класса. Не уверен, что это плохая практика. В любом случае, спасибо. - person bjskishore123; 19.10.2010