Грабли при инициализации ссылки умным указателем
От: Mazay Россия  
Дата: 29.09.07 12:58
Оценка: :)
Сделал для себя неприятное открытие. Хочу поделиться:

#include <iostream>
using std::cout;
using std::endl;

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;

struct A
{
    int magic;

    A(): magic(999)         // magic number
    {
        cout << "A::ctor()" << endl;
    }
    
    
    ~A() {cout<<"A::dtor()"<<endl;}
    
    void print_magic() { cout<<"go: " << magic << endl;}
};

struct B
{
    A &a;

    B(shared_ptr<A> spA):a(*spA) // !! инициализиуем ссылку умным указателем   :maniac: 
    {}
    B(B &b):a(b.a) {}
};

B make_B()
{
    shared_ptr<A> spA(new A);
    cout << "make_B" << endl;
    return B(spA);
}

int main()
{
    B b = make_B();
    cout << "Hi!" << endl;
    b.a.print_magic();
    cout << "Bye!" << endl;
    return 0;
}


Запускаем:
A::ctor()
make_B
A::dtor()
Hi!
go: 3277232       //а где же magic?
Bye!


Вот такие грабли. Так что даже с умными указателями и ссылками можно поиметь расстрел стэка.
Главное гармония ...
Re: Грабли при инициализации ссылки умным указателем
От: _nn_ www.nemerleweb.com
Дата: 29.09.07 13:02
Оценка:
Здравствуйте, Mazay, Вы писали:

Аксиома 1. Умные указатели не панацея от всех бед.

Вы перепишите без умного указателя и поймете в чем проблема:
...
    B(A& spA):a(spA) 
...

B make_B()
{
    A spA;
    cout << "make_B" << endl;
    return B(spA);
}


Аксиома 2. Сохранении на объект ссылки в классе требует, чтобы объект жил.

P.S.
Чем плохо хранить было умный указатель в классе ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Грабли при инициализации ссылки умным указателем
От: Mazay Россия  
Дата: 29.09.07 13:33
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Аксиома 1. Умные указатели не панацея от всех бед.

А я так надеялся.

__>P.S.

__>Чем плохо хранить было умный указатель в классе ?

Банально — a.print() короче чем a->print(). Как правило я храню ссылки на те объекты, которые хранитель не должен пережить по своему смыслу: вроде ссылок на родительские объекты, или ссылки на обрабатываемые объекты в хелперах и функторах. Потому там эти грабли просто не замечал. Но вот недавно призадумался.
Главное гармония ...
Re: Грабли при инициализации ссылки умным указателем
От: jazzer Россия Skype: enerjazzer
Дата: 29.09.07 13:42
Оценка: +2
Здравствуйте, Mazay, Вы писали:

M>Сделал для себя неприятное открытие. Хочу поделиться:

M>Вот такие грабли. Так что даже с умными указателями и ссылками можно поиметь расстрел стэка.

Ты делаешь концептуальную ошибку — отсюда все грабли.
Если у тебя есть объект, время жизни которого управляется умным указателем, поддерживающим недетерминированное удаление объекта (а именно таким является shared_ptr) — весь доступ к объекту должен осуществляться только через эти самые умные указатели. Осуществлять к нему доступ другими методами (например, через ссылку) можно только тогда, когда у тебя есть умный указатель на тот же самый объект, и который живет гарантированно дольше твоей ссылки (например, если эта ссылка локальная в данной области видимости, или если объект, содержащий ссылку — временный, как в случае функторов, которые ты передаешь в алгоритмы).
Если ты следуешь этому правилу (например, самое правильное решение в данном случае — держать в классе В не ссылку, а shared_ptr) — никаких проблем не будет.

И вообще, всегда, когда ты объявляешь в классе В член-ссылку или не умный указатель на класс А, и инициализируешь его чем-то пришедшим извне, ты фактический говоришь: класс В ссылается на объект класса А, временем жизни которого В не управляет, а это значит, что следить за тем, чтоб либо А гарантированно жил дольше, чем В, либо В гарантированно помер раньше, чем А, придется пользователю моего класса, и, скорее всего, вручную.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Грабли при инициализации ссылки умным указателем
От: bkat  
Дата: 29.09.07 14:12
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Если ты следуешь этому правилу (например, самое правильное решение в данном случае — держать в классе В не ссылку, а shared_ptr) — никаких проблем не будет.


А если в классе В держать "слабую ссылку" (weak_ptr), то будет еще лучше.
Re[3]: Грабли при инициализации ссылки умным указателем
От: jazzer Россия Skype: enerjazzer
Дата: 30.09.07 02:41
Оценка:
Здравствуйте, bkat, Вы писали:

B>Здравствуйте, jazzer, Вы писали:


J>>Если ты следуешь этому правилу (например, самое правильное решение в данном случае — держать в классе В не ссылку, а shared_ptr) — никаких проблем не будет.


B>А если в классе В держать "слабую ссылку" (weak_ptr), то будет еще лучше.


Зависит от того, что именно нужно.
Если нужно, чтоб объект жил — лучше держать shared_ptr.
Если нужно просто знать, помер объект или нет — weak_ptr.

В принципе, для извращенцев-оптимизаторов подойдет weak_ptr в отладочной сборке (просто вставить ассерт на разыменование) и просто указатель/ссылка в финальной.

Но это если по логике программы так надо.
Имхо, в исходной программе (судя по make_B) предполагалось, что объект должен жить, пока живет В, посему в этом данном случае надо держать shared_ptr
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Грабли при инициализации ссылки умным указателем
От: bkat  
Дата: 30.09.07 09:39
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, bkat, Вы писали:


B>>Здравствуйте, jazzer, Вы писали:


J>>>Если ты следуешь этому правилу (например, самое правильное решение в данном случае — держать в классе В не ссылку, а shared_ptr) — никаких проблем не будет.


B>>А если в классе В держать "слабую ссылку" (weak_ptr), то будет еще лучше.


J>Зависит от того, что именно нужно.

J>Если нужно, чтоб объект жил — лучше держать shared_ptr.
J>Если нужно просто знать, помер объект или нет — weak_ptr.

Это так, но именно ссылку логичнее заменить на weak_ptr.
Ссылка не управляет временем жизни объекта.
Re[5]: Грабли при инициализации ссылки умным указателем
От: jazzer Россия Skype: enerjazzer
Дата: 30.09.07 13:12
Оценка:
Здравствуйте, bkat, Вы писали:
B>Это так, но именно ссылку логичнее заменить на weak_ptr.
B>Ссылка не управляет временем жизни объекта.

(за исключением константной ссылки на rvalue)

Это зависит от того, что она собой олицетворяет.
Указатель тоже не управляет временем жизни объекта, если нет специального кода, который удаляет его. При этом остальные указатели объектом не управляют.
Т.е. по твоей логике получается, что если мы имеем код с указателями и ссылками, то механически заменить только один на shared_ptr, а остальные — на weak_ptr, что, очевидно, неверно.
Тем более, как сказал Мазай, он использует ссылку, а не указатель, просто потому, что рядом со ссылкой надо писать короткую точку, а не длинную стрелочку

Механический подход типа "вижу ссылку — меняю на weak_ptr" в общем случае не работает, надо исходить из общей инфраструктуры владения и времен жизни.
В данном конкретном случае (классы А и В и функция make_B, связывающая их и задающая схему владения) здесь должен быть shared_ptr.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Грабли при инициализации ссылки умным указателем
От: Аноним  
Дата: 01.10.07 19:23
Оценка:
Здравствуйте, Mazay, Вы писали:

M>Здравствуйте, _nn_, Вы писали:


__>>Аксиома 1. Умные указатели не панацея от всех бед.

M>А я так надеялся.

__>>P.S.

__>>Чем плохо хранить было умный указатель в классе ?

M>Банально — a.print() короче чем a->print().


Коллега, грешно экономить на спичках За что и поплатились.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.