2012-05-10 4 views
3

Я пришел через странный segfault. Причина на самом деле привело меня к ошибке, но я до сих пор не понимаю, почему ошибка сегментации вызывается здесь ... Код:Странный segfault с unique_ptr и shared_ptr

#include <memory> 
int main(int argc, char **arv) 
{ 
    int *i = new int; 
    std::unique_ptr<int> u1(i); 
    std::unique_ptr<int> u2; 
    u1 = std::move(u2); // line 7 
    std::shared_ptr<int> s1(i); // line 8 
    std::shared_ptr<int> s2; 
    s2 = s1; 
} 

компилировать с г ++ 4.6 и -std=c++0x и получить Segfault.

Если я сменил строку 7 на u2 = std::move(u1); (это была ошибка), она исчезает. Если я сменил строку 8 на std::shared_ptr<int> s1(new int(3)); (это, конечно, я не хочу), она также исчезает. Если я удалю из строки 8, также нет segfault.

Так что никакого вреда не было, но я не понимаю, почему должен быть segfault. Насколько я понимаю,
в строке 7 пустым указателем присваивается u1. Без сброса(), без конца. Тем не менее i кажется недействительным оттуда. Это намеренно? Это означает, что при перемещении указателя нужно быть очень осторожным, потому что другой объект может быть уничтожен!

Как вы думаете? Как мне защитить себя от этого?

Спасибо, Штеффен

ответ

11

Ваша линия 8 неправильно: После того, как вы захватить i в unique_ptr, вы не должны снова дать ему какой-то другой объект собственности взятием! Каждый владелец попытается удалить *i, что неверно.

Вместо этого вы должны создать общий указатель с указателем уникальный:

std::shared_ptr<int> s1(std::move(u2)); 

(Кроме того, у вас есть u1 и u2 навыворот.)

+1

Меня беспокоит то, что я могу сделать это, даже не предупреждая при компиляции с помощью '-pedantic -Wall -Wextra'. Действительно ли ответ «Не надо!»? – steffen

+2

@steffen: Действительно, ответ «Не надо». Нет защиты от 'int * p = new int; do_crazy_stuff (р); be_insane (р); take_ownership (р); '. Компилятор не может действительно знать, что вы собираетесь делать с указателем. –

+0

Правда ... спасибо за де-заблуждение :) – steffen

3

Эта линия:

u1 = std::move(u2); 

Делает предыдущий указатель сохраненную u1 быть удалены. Поэтому ваш i указатель освобождается. Создание shared_ptr, содержащего указатель free'd, является неопределенным поведением.

+0

да. Я понял это. Смотрите мой комментарий к Kerrek: никаких предупреждений. Теперь компилятор мне навредит! – steffen

+0

Нет ничего плохого в назначении уникальных указателей в любом случае. Уникальный указатель, построенный по умолчанию, имеет значение NULL, а назначение правильно распределяет старый ресурс. Строка 'u1 = std :: move (u2);' удаляет '* i', а затем оба' u1' и 'u2' равны нулю. –

+0

@KerrekSB Я никогда не говорил, что что-то не так с назначением unique_ptr. То, как OP это делает, указатель, который держит 'u1', свободен после переадресации' u2'. Проблема в том, что он создает shared_ptr для указателя (уже свободного). Steffen, компилятор не имеет возможности узнать, что делает shared/unique_ptr делать то, что указатели они хранят. – mfontanini