Синтаксические рекомендации по владению и выпуску объектов в C ++ - PullRequest
7 голосов
/ 03 ноября 2011

Я хочу знать - есть ли какие-либо рекомендации по синтаксису функций C ++ (не) -членов, которые позволяют мне (без комментариев, если это возможно) понимать политику владения своими аргументами и возвращаемым значением. Под собственностью я подразумеваю, что владелец несет ответственность за уничтожение принадлежащего ему объекта.

Я различаю следующие правила относительно аргументов:

  • стать владельцем
  • не берут на себя ответственность
  • Доля

и о возвращаемом значении:

  • release ('возврат по значению' находится в этой группе)
  • не выпускать
  • Доля

Например, передача объекта по ссылке не требует его владения:

void func(object & obj) { ... }

В таких рекомендациях могут использоваться стандартные конструкции, такие как unique_ptr, shared_ptr и т. Д. Если таких рекомендаций нет, то примеры возможных недоразумений синтаксиса также приветствуются.

Ответы [ 3 ]

8 голосов
/ 03 ноября 2011

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

  • auto_ptr / unique_ptr - один владелец, право собственности передается
  • shared_ptr - несколько владельцев, собственностьможет быть передан
  • scoped_ptr - один владелец, право собственности не может быть передано
  • weak_ptr - наблюдатель (но shared_ptr может быть создан из weak_ptr)

Я думаю, что этого достаточно, чтобы четко показать обязанности, например,

void func(std::auto_ptr<Class> input) {...} // func() takes ownership of input
void func(std::shared_ptr<Class> input) {...} // func() and caller share ownership
std::auto_ptr<Class> func() {...} // caller takes ownership of returned value
std::shared_ptr<Class> func() {...} // func() and caller shares ownership of returned object
std::weak_ptr<Class> func() {...} // func() owns created object, but caller may observe it

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

Примечание!Если вы используете C ++ до 11, вам придется прибегнуть к boost::shared_ptr и boost:weak_ptr.

0 голосов
/ 03 ноября 2011

Если я вас понимаю, тогда Boost call_traits может быть тем, что вы ищете:

Пример (скопированный из документации идет):

template <class T>
struct contained
{
   // define our typedefs first, arrays are stored by value
   // so value_type is not the same as result_type:
   typedef typename boost::call_traits<T>::param_type       param_type;
   typedef typename boost::call_traits<T>::reference        reference;
   typedef typename boost::call_traits<T>::const_reference  const_reference;
   typedef T                                                value_type;
   typedef typename boost::call_traits<T>::value_type       result_type;

   // stored value:
   value_type v_;

   // constructors:
   contained() {}
   contained(param_type p) : v_(p){}
   // return byval:
   result_type value() { return v_; }
   // return by_ref:
   reference get() { return v_; }
   const_reference const_get()const { return v_; }
   // pass value:
   void call(param_type p){}

};

Не намного яснее, чем param_type, reference_type и return_type, чтобы указать, что имеется в виду.

0 голосов
/ 03 ноября 2011

Я просто использую этот синтаксис для параметров, где это необходимо:

пример объявления конструктора:

t_array(const t_ownership_policy::t_take& policy, THESpecialType* const arg);

Используется на месте вызова:

t_array array(t_ownership_policy::Take, THESpecialTypeCreate(...));

Где t_ownership_policy::t_take - это просто имя типа двусмысленной перегрузки с перегрузкой.

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

Для «возвращения»:

void func(t_container<t_type>& outValue);

Где t_container - какой бы тип контейнера указателя вы не выбрали. Тогда тип контейнера уже реализует необходимый шаблон. Этот контейнер может быть чем-то вроде shared_ptr, или какой-то специализацией, которую вы написали.

и для более сложных типов, я часто буду использовать синтаксис так:

void func(t_special_container& outValue) {
  ...
  outValue.take(ptr);
  - or -
  outValue.copy(ptr);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...