Лучше вернуть ссылку на C ++ или слабый_птр? - PullRequest
10 голосов
/ 18 октября 2008

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

class Member;

class ClassWithWeakPtr
{
private:
   boost::shared_ptr<Member> _member;
public:
   boost::weak_ptr<Member> GetMember();
};

или

class Member;

class ClassWithCppReference
{
private:
    Member _member;
public:
    Member& GetMember() {return _member;}
};

Что ты думаешь? Когда один лучше другого?

Ответы [ 9 ]

13 голосов
/ 18 октября 2008

Почему бы не вернуть shared_ptr<>? Таким образом, клиент получает возможность использовать то, что возвращается, до тех пор, пока он ему нужен, но нет никаких проблем, если класс 'server' исчезнет.

Существует не так много ситуаций, в которых семантика weak_ptr<> имеет большой смысл (кеширование и ???). Обычно, когда клиент запрашивает что-либо, он хочет, чтобы право собственности на эту вещь определялось самим клиентом (а совместное владение так же хорошо, как и полное владение).

Если у вас есть ситуация, когда объект «сервер» может быть уничтожен без ведома клиента (ситуация, когда вам может потребоваться use weak_ptr<> или shared_ptr<>) о худшем, что вы можете сделать, это вернуть ссылку на член. В этом случае клиент не может знать, безопасно ли получить доступ к возвращенной ссылке. Вы должны вернуть копию члена или умный указатель, который может правильно управлять временем жизни возвращаемого члена.

Помните, что в случае, когда есть выражение, которое создает временный ClassWithCppReference (что не всегда очевидно), клиент, который вызывает GetMember(), даже не сможет использовать возвращенную ссылку в следующем операторе.

5 голосов
/ 19 октября 2008

Я хочу поднять что-нибудь в ответ на комментарии (в основном от OP и Colomon) об эффективности и т. Д. Зачастую копирование материалов действительно не имеет значения, с точки зрения реальной производительности.

Я написал программы, которые делают много защитного копирования . Это идиома в Java, где, поскольку все объекты передаются по указателю, происходит много алиасов, поэтому вы копируете все, что входит в / из вашего класса, чтобы нарушить алиасинг, гарантируя, что клиенты вашего класса не смогут нарушить инварианты вашего класса изменяя объект по факту.

Программы, которые я написал, защитно копировали целые структуры местами, включая целые списки и карты. Просто чтобы быть уверенным, что производительность не пострадает, я подробно описал программу. Основные узкие места были в другом месте (и даже тогда я настроил эти другие места так, чтобы основным узким местом оставалась сеть). Нигде это защитное копирование фигуры в горячие точки программы.


ETA: Я чувствую необходимость уточнить смысл моего сообщения, поскольку один комментатор прочитал его не так, как я планировал, и вполне возможно, что другие тоже это сделали. Моя точка зрения не в том, что это нормально копировать вещи вокруг волей-неволей; скорее, всегда следует профилировать производительность их кода, а не гадать, как он будет работать.

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

4 голосов
/ 18 октября 2008

Вы должны избегать раздачи своих внутренних органов; это руководство п. 42 «Стандартов кодирования C ++» (Херб Саттер и Андрей Александреску). Если по каким-либо причинам вам нужно, лучше вернуть константную ссылку , а не указатель, потому что constness не распространяется через него.

weak_ptr <> представляется жизнеспособным решением, даже если его основная цель - избежать циклов shared_ptr. Вместо этого, если вы возвращаете shared_ptr <>, вы продлеваете срок службы такого внутреннего, что в большинстве случаев не имеет смысла.

Проблема с экземпляром, который исчезает, когда кто-то обрабатывает ссылку на его внутреннее устройство, должна столкнуться с правильной синхронизацией / связью между потоками.

2 голосов
/ 19 октября 2008

Я думаю, что единственный разумный ответ - это зависит от того, как Член связан с Классом, и что вы хотите, чтобы пользователи Класса могли делать. Имеет ли _member осмысленное существование, которое не зависит от объекта Class? Если это не так, то я не думаю, что использование shared_ptr имеет смысл, возвращаете ли вы weak_ptr или shared_ptr. По сути, любой из них предоставил бы пользователю доступ к объекту Member, который мог бы пережить объект Class, который придает ему значение. Это может предотвратить сбой, но ценой сокрытия серьезной ошибки проектирования.

Как указывает awgn, вы должны быть очень осторожны при разоблачении внутренних элементов вашего класса. Но я думаю, что определенно есть место для этого. Например, в моем коде есть класс, представляющий объект файла, состоящий из объекта заголовка файла и объекта данных файла. Полное дублирование интерфейса заголовка в классе файлов было бы глупо и нарушало бы СУХОЙ. Вы могли бы, я полагаю, заставить пользователя получить копию объекта заголовка, внести изменения в копию, а затем скопировать внешний объект обратно в общий класс файла. Но это приводит к большой неэффективности, которая только дает вам возможность отличить представление файлового объекта заголовка от представления объекта заголовка. Если вы уверены, что это не то, что вам нужно, вы можете также вернуть неконстантную ссылку на заголовок - это проще и эффективнее.

1 голос
/ 22 марта 2009

Зачем возвращать слабый указатель? Я думаю, что вы делаете это более сложным без необходимой выгоды.

1 голос
/ 22 марта 2009

Возврат слабого указателя определенно будет дороже и не будет служить никакой реальной цели - вы все равно не сможете удержать владельца в собственности дольше, чем срок службы основного объекта.

0 голосов
/ 22 июля 2009

В большинстве случаев я бы предоставил

const Member& GetMember() const;
Member& GetMember(); // ideally, only if clients can modify it without
                     // breaking any invariants of the Class

weak_ptr< Member > GetMemberWptr(); // only if there is a specific need
                                    // for holding a weak pointer.

Основанием для этого является то, что (я и, вероятно, многие другие) вызов метода GetMember() подразумевает, что Class владеет member и, следовательно, возвращаемая ссылка будет действителен только в течение времени жизни содержащего объекта.

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

0 голосов
/ 19 октября 2008

В зависимости от контекста может подойти любой из них. Основная проблема с возвратом «живой» ссылки члену (если вам необходимо предоставить ее в первую очередь) заключается в том, что тот, кто использует открытый член, состоит в том, что ваш клиент может удерживать его дольше, чем существует содержащий объект. И если ваш клиент обращается к указанному члену через ссылку, когда содержащий его объект выходит из области видимости, вы столкнетесь с «странными» сбоями, неправильными значениями и подобным весельем. Не рекомендуется.

Возврат слабого_птра <> имеет главное преимущество, заключающееся в том, что он сможет сообщить клиенту, что объект, к которому он пытается получить доступ, пропал, чего не может сделать ссылка.

Стоимость моих 2 копеек будет:

  • Если ни один из ваших клиентов никогда не будет использовать член, вы, как автор, будете единственным, кто будет использовать его и контролировать время жизни объекта, возвращая ссылку - это нормально. Ссылка Const была бы еще лучше, но это не всегда возможно в реальном мире с существующим кодом.
  • Если кто-то еще получит доступ к члену и будет использовать его, особенно если вы пишете библиотеку, верните слабый_птр <>. Это избавит вас от горя и облегчит отладку.
  • Я бы не вернул shared_ptr <>. Я видел такой подход, и его обычно одобряют люди, которым не нравится концепция «слабый_птр / слабый». Главный недостаток, который я вижу в этом, заключается в том, что он искусственно продлит срок жизни члена другого объекта за пределы объема содержащего его объекта. Это концептуально неправильно в 99% случаев и даже хуже, может превратить вашу программную ошибку (доступ к чему-то, чего там больше нет) в концептуальную ошибку (доступ к чему-то, чего больше не должно ). 1014 *
0 голосов
/ 18 октября 2008

Мои основные невежественные указания:

Я понимаю, что последнее может быть небезопасно, если экземпляр ClassWithCppReference может исчезнуть, в то время как у кого-то еще есть ссылка на члена. Однако я также вижу аргумент для последнего класса с типами POD, например, например, сообщение на устройство, и вы не хотите постоянно копировать элемент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...