Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / C++ Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 shared_ptr как возвращаемый результат  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1679
сомнения меня гложут, как правильно делать?

раньше, если я возвращал какую-то переменную класса геттером, я делал так:
const T& A::Get() {...}

теперь, если у меня в классе переменная идёт, как shared_ptr<T>, то я и должен возвращать ровно shared_ptr<T>, чтобы создалась ещё одна копия, и счётчик таким образом увеличился на 1.
shared_ptr<T> A::Get() {...}

если же я сделаю так:
shared_ptr<T>& A::Get() {...}

то это выстрел в ногу, так как счётчик не изменится.

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

Как сложить эти две вещи вместе, возвращать ссылку на объект(это быстро) и авто-контролировать счётчик (это безопасно)? Как вообще правильно работать в данном случае с shared_ptr<T> снаружи классов?
16 июн 17, 09:27    [20569055]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
CEMb, ты просто забыл включить мозг. ;)
Если ты объявил переменную типа shared_ptr<T> на стеке внутри метода, то после выхода из метода счетчик останется неизменным, так как предыдущий экземпляр shared_ptr<T> будет уничтожен деструктором и счетчик уменьшен на ту же единицу.
Если ты объявил "shared_ptr<T>" на стеке, то вернуть его как "shared_ptr<T>&" ты не можешь, так как, опять же, его экземпляр будет разрушен после выхода из области видимости. Возвращать его как "shared_ptr<T>&" ты можешь, например, если объявил этот указатель как "static shared_ptr<T>&".
16 июн 17, 09:46    [20569104]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
PPA
Member

Откуда: Караганда -> Липецк
Сообщений: 759
rdb_dev
CEMb, ты просто забыл включить мозг. ;).

он пишет про член - где вы увидели возврат ссылки на локальную переменную?

2CEMb - геттер у тебя ведь простой без логики?
зачем он у тебя лежит в cpp? помести его в h и он заинлайнится оптимизатором.
16 июн 17, 09:59    [20569134]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
PPA
он пишет про член - где вы увидели возврат ссылки на локальную переменную?
Здесь подразумевается возврат экземпляра класса-указателя на член типа "T" экземпляра класса "A", а не то, что "shared_ptr<T>" объявлен как член класса. Здесь "T", это тип члена класса "A", а "shared_ptr<T>", это подобие "T*" или "T&", но если два последних, это простые типы указателя и ссылки на "T", то "shared_ptr<T>", это класс указателя (некая реализация сборки мусора).
16 июн 17, 10:27    [20569214]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
PPA, https://ru.wikipedia.org/wiki/Умный_указатель
16 июн 17, 10:32    [20569233]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
YesSql
Guest
CEMb,

передача по значению - есть передача по значению. И в случае с shared_ptr она может быть достаточно тяжёлой.
16 июн 17, 10:37    [20569255]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 43944

YesSql
передача по значению - есть передача по значению. И в случае с shared_ptr она может быть
достаточно тяжёлой.

Во-первых, современные компиляторы делают copy elimination, что сильно облегчает процесс.
Во-вторых, современная парадигма С++ игнорирует вопросы быстродействия в пользу легко
читаемого кода.

Posted via ActualForum NNTP Server 1.5

16 июн 17, 10:47    [20569314]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1679
rdb_dev
CEMb, ты просто забыл включить мозг. ;)
я не могу включить то, чего нет, поэтому и пришёл с вопросом.
PPA
зачем он у тебя лежит в cpp?
а как по коду ты понял, где он лежит? Картинка с другого сайта.
rdb_dev
Здесь подразумевается возврат экземпляра класса-указателя на член типа "T" экземпляра класса "A", а не то, что "shared_ptr<T>" объявлен как член класса.
ненене, именно объявлен как shared_ptr<T>, но если это пагубная практика, то расскажите, как правильно. Имхо, неправильно отдавать кому-то shared_ptr<T> если сам объект shared_ptr<T> не является. Грубо говоря, управление памятью должно быть единообразно. Иначе, как вариант: если после геттера объект класса А будет уничтожен, код, получивший shared_ptr<T> не узнает об этом и помешать этому не сможет.

Заново.
class A {
shared_ptr<T> t_;
shared_ptr<T> Get() {return t_;}
};
Get() создаёт копию объекта shared_ptr<T>, который инициализируется указателем на t_, т.е. с копирование адреса t_ у нас тут проблем нет, но вот с вызовом конструктора shared_ptr<T> - как бы накладные расходы, да? Учение говорит, что возвращать надо ссылки(или указатели, когда нельзя ссылки), иначе будет неявно зваться конструктор.
Но (тот самый вопрос), если мы сделаем так:
shared_ptr<T>& Get() {return t_;}
то получим ссылку на t_, копирование указателя пройдёт быстро, никаких лишних вызовов и кода (как велит учение), но, опять же, если объект класса А будет уничтожен после геттера, геттер узнает об этом(хоть с трудом, но можно), но помешать этому не сможет.

Вопрос в том: как теперь, в связи с shared_ptr надо отдавать переменные класса. Ну или в обратную сторону: как описывать входящие параметры типа shared_ptr<T> в функциях. Если как ссылки - быстро и ненадёжно, если как объекты, то надёжно и небыстро.
16 июн 17, 10:50    [20569325]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1679
YesSql
передача по значению - есть передача по значению. И в случае с shared_ptr она может быть достаточно тяжёлой.
Эээ тяжёлой, но не достаточно тяжёлой. Это просто вызов конструктора shared_ptr<T> и передача ему указателя на объект. Т.е. тяжело, но не сильно. Но передача по ссылке - всё равно легче.
Dimitry Sibiryakov
Во-первых, современные компиляторы делают copy elimination, что сильно облегчает процесс.
А, блин, точно, там оптимизация по возвращаемому значению же... я забыл про это :(
Ну, т.е. мой вариант
shared_ptr<T> Get()
- абсолютно верный? :)
Dimitry Sibiryakov
Во-вторых, современная парадигма С++ игнорирует вопросы быстродействия в пользу легко
читаемого кода.
но при этом авторы сделали очень много, чтобы увеличить это самое быстродействие. Ладно, не будем вдаваться в детали, а то сщас начнётся...
16 июн 17, 10:55    [20569346]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
OoCc
Member

Откуда: с Кавказа
Сообщений: 1329
CEMb
YesSql
передача по значению - есть передача по значению. И в случае с shared_ptr она может быть достаточно тяжёлой.
Эээ тяжёлой, но не достаточно тяжёлой. Это просто вызов конструктора shared_ptr<T> и передача ему указателя на объект. Т.е. тяжело, но не сильно. Но передача по ссылке - всё равно легче.

Все зависит от задачи. Это не "просто вызов конструктора" это атомик или мютекс в зависимости от имплементации.
16 июн 17, 11:06    [20569379]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1679
OoCc
Все зависит от задачи. Это не "просто вызов конструктора" это атомик или мютекс в зависимости от имплементации.
Ну ок. Так как тогда правильно возвращать значения и передавать параметры в функции? Или зависит от типа?
16 июн 17, 11:18    [20569432]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
OoCc
Member

Откуда: с Кавказа
Сообщений: 1329
CEMb
OoCc
Все зависит от задачи. Это не "просто вызов конструктора" это атомик или мютекс в зависимости от имплементации.
Ну ок. Так как тогда правильно возвращать значения и передавать параметры в функции? Или зависит от типа?

Прелесть С++ в том что все зависит от задачи и ее дизайна. С++ может быть Java-ой а может быть и C с-классами.
16 июн 17, 11:35    [20569542]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
CEMb
Имхо, неправильно отдавать кому-то shared_ptr<T> если сам объект shared_ptr<T> не является. Грубо говоря, управление памятью должно быть единообразно. Иначе, как вариант: если после геттера объект класса А будет уничтожен, код, получивший shared_ptr<T> не узнает об этом и помешать этому не сможет.
Если ты самостоятельно разрушаешь объект, то shared_ptr об этом, по-любому, никак не узнает. Если ты используешь shared_ptr<T>, ни о каком самостоятельном разрушении экземпляра "T", на который ссылается экземпляр "shared_ptr<T>" не может быть и речи. При использовании "умных указателей", без накладных расходов не обойтись, но можно сократить количество созданий и разрушений shared_ptr при передаче его в качестве результата. Возвращай из геттера как "shared_ptr<T>&" - по ссылке и не парься! Так как в коде ты всё равно будешь делать что-то типа:
shared_t<T> t = instanceA->Get();
16 июн 17, 11:47    [20569590]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
Ошибся в написании:
shared_ptr<T> t = instanceA->Get();
16 июн 17, 12:03    [20569658]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1679
OoCc
Прелесть С++ в том что все зависит от задачи и ее дизайна.
так мне-то в вышеприведённом коде, без atomic и mustex, как лучше делать?
rdb_dev
Возвращай из геттера как "shared_ptr<T>&" - по ссылке и не парься!
Нет, так неправильно. Пример:
auto t = a->Get();
delete a;
t.get(); // UB!
16 июн 17, 12:11    [20569695]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
OoCc
Member

Откуда: с Кавказа
Сообщений: 1329
CEMb
OoCc
Прелесть С++ в том что все зависит от задачи и ее дизайна.
так мне-то в вышеприведённом коде, без atomic и mustex, как лучше делать?
rdb_dev
Возвращай из геттера как "shared_ptr<T>&" - по ссылке и не парься!
Нет, так неправильно. Пример:
auto t = a->Get();
delete a;
t.get(); // UB!

С таким дизайном тебе больше подходит Java-подобная парадигма С++ 20569314
16 июн 17, 12:27    [20569764]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
CEMb
rdb_dev
Возвращай из геттера как "shared_ptr<T>&" - по ссылке и не парься!
Нет, так неправильно. Пример:
auto t = a->Get();
delete a;
t.get(); // UB!
Что именно неправильно? Если экземпляр "T" не является инкапсулированным в "A", то удаление экземпляра "A" приведет к удалению "умной ссылки", а не самого экземпляра "T", при условии, что где-либо существует еще одна валидная ссылка "shared_ptr<T>" на данный экземпляр "T". Пример:
class A
{
private:
  shared_ptr<T> t_;

public:

  shared_ptr<T>& A::Get()
  {
    if (NULL == this->t_.get()) this->t_.reset(new T());
    return this->t_;
  }
};

A* a = new A();
shared_ptr<T> t = a->Get();
delete a;
t->someMethod(); // SUCCESS!
16 июн 17, 13:11    [20569899]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
Чтоб было понятнее, перепишем пример так:
class A
{
private:
  shared_ptr<T> t_;

public:

  shared_ptr<T>& A::Get()
  {
    if (NULL == this->t_.get()) this->t_.reset(new T());
    return this->t_;
  }
};

A* a = new A();
shared_ptr<T>* ppT = new shared_ptr<T>(a->Get());
delete a;
(*ppT)->someMethod();  // SUCCESS!
delete ppT;
(*ppT)->someMethod();  // FAIL!
16 июн 17, 13:21    [20569940]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
точнее так:
A* a = new A();
shared_ptr<T>* ppT = new shared_ptr<T>(a->Get());
delete a;
(*ppT)->someMethod();  // SUCCESS!
T* t = (*ppT).get();
delete ppT;
t->someMethod();  // FAIL!
16 июн 17, 13:26    [20569969]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 5990
Можно возвращать ссылку на смартуказатель.
А если вызывающему коду надо продлить время жизни объекта по этой ссылке, то там надо положить эту ссылку в копию смартуказателя.
16 июн 17, 14:37    [20570207]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
Вася Уткин
Guest
Если тебе не нужно делать очень часто так:
a->Get()->some();                 // так
shared_ptr<T> &ptr = a->Get();    // или так (что в большинстве случаев плохая практика)
тогда возвращай по значению shared_ptr<T> A::Get() {...}

Возвращение shared_ptr по ссылке несет больше потенциальных проблем, чем по значению. Но в некоторых случаях может быть быстрее.
18 июн 17, 22:56    [20573430]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1679
rdb_dev
shared_ptr<T> t = a->Get();
интересный момент. Я имел ввиду:
shared_ptr<T>& t = a->Get();
И тогда delete a; уничтожит и t_ тоже (по идее должно, не проверял)
Или, иными словами:
Anatoly Moskovsky
А если вызывающему коду надо продлить время жизни объекта по этой ссылке, то там надо положить эту ссылку в копию смартуказателя.
Т.е. получается, что если мы возвращаем ссылку на shared_ptr, мы оставляем на совести вызывающего следить за тем, жив a в момент вызова Get() или нет.
Ну ок, мне этот вариант больше нравится, спасибо всем :)
Пойду переписывать код...
19 июн 17, 05:38    [20573562]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
CEMb
Т.е. получается, что если мы возвращаем ссылку на shared_ptr, мы оставляем на совести вызывающего следить за тем, жив a в момент вызова Get() или нет.
Ну ок, мне этот вариант больше нравится, спасибо всем :)
Пойду переписывать код...
Ну да! Представь, что тебе, по какой-то причине, понадобилось вызвать a->Get() вхолостую - без присваивания значения в вызывающей программе, чтобы инициализировать объект "T" заранее. При этом, если "shared_ptr<T>" будет возвращаться по значению, то объект будет создан дважды - в куче и на стеке (конструктором копии).
19 июн 17, 09:29    [20573714]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 1615
CEMb
Я имел ввиду:
shared_ptr<T>& t = a->Get();
И тогда delete a; уничтожит и t_ тоже (по идее должно, не проверял)
Обязано! Так как "shared_ptr<T>" является инкапсулируемым, он будет разрушен при разрушении экземпляра "A" автоматически сгенерированным для этой цели кодом.
19 июн 17, 10:18    [20573824]     Ответить | Цитировать Сообщить модератору
 Re: shared_ptr как возвращаемый результат  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 5990
CEMb
Т.е. получается, что если мы возвращаем ссылку на shared_ptr, мы оставляем на совести вызывающего следить за тем, жив a в момент вызова Get() или нет.
Ну ок, мне этот вариант больше нравится, спасибо всем :)
Пойду переписывать код...

Если программа многопоточная, то возврат ссылки (а не копии) создает точку, когда объект может быть уничтожен между возвратом и присвоением его в копию.
Поэтому, если объект используется в нескольких потоках, то ссылки использовать нельзя (кроме случаев когда вы другими способами гарантируете время жизни объекта).
Создание же копии смартуказателя это атомарная операция и гарантирует что другой поток не уничтожит объект.
19 июн 17, 13:52    [20574558]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / C++ Ответить