2016-12-21 9 views
1

У меня есть следующий простой код, и я не нашел нить, где умные указатели используются для этого простого случая, а копировать объекты:Интеллектуальные указатели - unique_ptr для переменной в стеке выделяется

int main() 
{ 
    int i = 1; 

    std::unique_ptr<int> p1(&i); 
    *p1 = 2; 

    return 0; 
} 

Этот приводит к ошибке _BLOCK_TYPE_IS_INVALID, когда указатель выходит из сферы видимости. Если я звоню p1.release(), код работает нормально, и я не получаю эту ошибку. Я думал, что такие указатели достаточно умны, чтобы обращаться с оборванными указателями?

Альтернативой было бы, если бы я была копия I, которая не дает вышеуказанную ошибку:

std::unique_ptr<int> p1(new int(i)); 

Что такое преимущество использования смарт-указатели здесь, где я не хочу, чтобы выполнить локальный копировать?

Если бы я должен был использовать сырой указатель:

int i = 1; 
int *p1 = &i; 
*p1 = 2; 

Я не получаю сообщение об ошибке, даже если я не код:

p1 = nullptr; 

ответ

8

std::unique_ptr s предназначены для обработки динамически выделенной памяти ; они говорят, что берут на себя права собственности указателя при его создании.

Ваш int i; находится в стеке и, следовательно, не динамически распределяется; его собственность не может быть удалена из стека. Когда деструктор unique_ptr пытается указать delete указатель, который вы ему дали (поскольку он думает, что его никто больше не владеет), вы получаете эту ошибку, потому что delete может использоваться только для указателей, созданных с помощью new.

Учитывая ваш короткий пример, я не знаю, в каком контексте это ... Но вы должны использовать необработанные указатели (или сделать копию, как вы сказали), для переменных, распределенных по стекам.

+0

Благодарю вас обоих, это имеет смысл. Кроме того, почему я не могу просто p1 = nullptr, а не p1.release(). Я понимаю, что release() возвращает указатель на управляемый объект и освобождает право собственности. p1.reset() заменяет управляемый объект, поэтому имеет смысл, что это незаконно для переменной, присвоенной стеку. Но почему p1 = nullptr компилируется, но вызывает исключение? – c0der

+1

Назначение 'unique_ptr' с [' = '] (http://www.cplusplus.com/reference/memory/unique_ptr/operator=/) в основном совпадает с вызовом [' reset'] (http: // www .cplusplus.com/reference/memory/unique_ptr/reset /) и уничтожает ранее сохраненный указатель. Использование ['release'] (http://www.cplusplus.com/reference/memory/unique_ptr/release/) оставляет владение указателем, не уничтожая его. – qxz

+0

'p1 = nullptr' является назначением, p1 должен очистить свой указатель, а затем удерживать nullptr. Это то, что должен делать владелец. 'p1.release()' состоит в том, чтобы отдать свое «право собственности» и вернуть указатель на него другим, и больше не управлять, чтобы он не очистил его. –

0

Ответ предоставляется @qzx является основной причиной этой проблемы вы сообщили в своем коде

Одна дополнительная вещь std::unique_ptr<> использования. Вы должны всегда пытаться использовать std::make_unique<>() для создания std::unique_ptr. Иногда это помогает правильно использовать этот указатель, предоставляя соответствующие ошибки компиляции.

Например, если изменить приведенный выше код с std::make_unique<> в

int i = 1; 
std::unique_ptr<int> p1 = make_unique<int>(&i); 

, то большинство компилятор выдаст сообщение об ошибке, как

can't convert int* to int 

Это поможет вам использовать копию, которая создает совершенно новый целое число, право собственности на которое принадлежит std::unique_ptr<>

int i = 1; 
std::unique_ptr<int> p1 = make_unique<int>(i); 
*p1 = 2; 

Этот код работает.

+0

Таким образом, нет смысла использовать интеллектуальный указатель в этом случае, в отличие от ссылки на i, int & p = i, так как & p разыменовывается, когда он выходит за пределы области видимости. Я думаю, лучше использовать интеллектуальный указатель в объявлении класса, например. make_unique доступен в C++ 14? Мой компилятор VS2012 не распознает его. – c0der