boost :: shared_ptr вопрос. Почему это работает? - PullRequest
3 голосов
/ 08 декабря 2010

Экспериментируя с этим вопросом я создал пример, который мне совершенно не понятен.В частности, это подчеркивает мое неправильное понимание указателей, ссылок и boost :: shared_ptr.

int& r = *(new int(0));//gratuitous pointer leak, got to initialize it to something
{
    boost::shared_ptr<int> sp(new int(100));
    r = *sp;
    cout << "r=" << r << endl;
}
cout << "r=" << r << endl << endl;

int* p;
{
    boost::shared_ptr<int> sp(new int(100));
    p = &*sp;
    cout << "*p=" << *p << endl;
}
cout << "*p=" << *p << endl;

Запуск этого кода дает вывод примерно такой:

r=100
r=100

*p=100
*p=13

Почемуссылка переживет смерть shared_ptr, а указатель - нет?


В ответах здесь есть проблема, заключающаяся в том, что существуют два диаметрально противоположных и противоречивых решения и нет единого мнения о том, что является правдой.Мне бы хотелось иметь возможность использовать ссылку после удаления shared_ptr, но если она недействительна, мне действительно нужно это понять.

Возможно, кто-то может опубликовать простой пример, демонстрирующий неопределенное поведение в ссылке.

Ответы [ 3 ]

12 голосов
/ 08 декабря 2010

Потому что r = *sp; не делает то, что вы думаете, что делает. Он присваивает референт , то есть анонимному int объекту, созданному вами в куче в строке 1. Вы не можете пересылать ссылки в C ++.

Вот что говорит стандарт об оценке ссылочных выражений:

Если выражение изначально имеет тип «ссылка на T», тип корректируется до T перед любым дальнейшим анализом. Выражение обозначает объект или функцию, обозначаемую ссылкой , и выражение является lvalue или xvalue, в зависимости от выражения.

Итак, вы видите, что нет способа добраться до "самой ссылки". Его просто нет в C ++.

Может быть, этот код сделает его более понятным:

int a = 42;
int b = 97;

int&r = a;   // r is just an alias (another name) for a
    r = b;   // assigns b to a (does NOT bind r to b, that's impossible in C++!)

После выполнения последней строки, оба a и b содержат 97, потому что r = b действительно означает a = b.

2 голосов
/ 08 декабря 2010

p не определено, r является копией

int& r = *(new int(0));
{
    boost::shared_ptr<int> sp(new int(100));
    r = *sp; // copy
    cout << "r=" << r << endl;
}
cout << "r=" << r << endl << endl;

int* p;
{
    boost::shared_ptr<int> sp(new int(100));
    p = &*sp;
    cout << "*p=" << *p << endl;
}
cout << "*p=" << *p << endl; // Undefined, pointer points to deleted int
0 голосов
/ 08 декабря 2010

Во втором случае ваш int -объект уничтожен.В первом случае это не так.

В первом случае вы создаете новый int -объект с new во внешней области видимости.Во внутренней области вы создаете второй int -объект, для которого вы также создаете shared_ptr, которому затем принадлежит int -объект.Это shared_ptr выходит из области видимости при закрытии внутренней области видимости, поэтому оно разрушается.Деструктор shared_ptr также будет уничтожать объект, к которому он относится, потому что никакой другой shared_ptr (созданный из исходного) больше не ссылается на ваш объект int.Все в порядке.Тем не менее, в середине этой области вы переназначаете значение r на значение *sp (100).Поэтому вы сохраняете значение *sp до разрушения sp в r.

Примечание: это, безусловно, сомнительный стиль - создавать объект int так, как вы делаете это в первой строкекода.Если вы явно не удалите этот объект int, это память.Уничтожить его можно было бы с помощью delete &r, который выглядит действительно ужасно, особенно потому, что символ r впоследствии все еще ссылается на теперь удаленный объект int.НЕ ДЕЛАЙТЕ ЭТОГО!

Во втором случае вы создаете указатель int в начале, но не объект int.Внутренняя область видимости почти такая же, как и раньше, за исключением того, что на этот раз вы не сохраняете значение вашего нового объекта int во внешней области видимости (p), но сохраняете адрес объекта int!Поскольку объект int разрушается в конце внутренней области (по той же причине, что и ранее), p больше не указывает на существующий объект int, а на место в памяти, которое ранее когда-то содержало int объект.Значение, которое вы получаете из *p, не определено: вы все равно можете получить 100, вы можете получить любое другое значение, и вы можете даже вызвать сбой вашей программы здесь (ошибка сегментации), поскольку вы разыменовываете ячейку памяти, которую вы больше не держите.

Итак, подведем итоги и ответим на последний вопрос:

Ссылка сохраняется, поскольку она все еще ссылается на существующий объект.Указатель этого не делает, поскольку он указывает на более не существующий объект.

...