Выразите использование аргументов C ++ через интерфейсы методов - PullRequest
5 голосов
/ 26 января 2009

Есть ли общий способ выразить использование аргументов в C ++? Я хочу неявно сказать потребителям моего класса, как передаваемые им аргументы будут использоваться классом.

Примеры:

  1. Я владею вашим аргументом (приведу в порядок)
  2. Я буду хранить ссылку на ваш аргумент при моей жизни (поэтому вы НЕ должны удалять его, пока я жив)
  3. Я буду использовать ваш аргумент только во время построения и не буду содержать ссылку

Есть ли распространенный способ выразить эти вещи, просто используя объявление метода? Я думаю, что в первом случае std :: auto_ptr будет иметь смысл. Во втором случае я обычно беру указатель, чтобы избежать передачи кем-либо значения из стека, которое быстро сделает недействительной мою ссылку, или альтернативно shared_ptr. В третьем случае я беру ссылку, чтобы разрешить значения из стека.

Как вы справляетесь с этим? Также необходимо ли полагаться на умные указатели здесь, или можно выразить такие вещи, просто используя каким-то образом голые ссылки и указатели?

Ответы [ 7 ]

6 голосов
/ 26 января 2009

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

1 - аргумент auto_ptr означает, что класс будет управлять управлением памятью для объекта. (Мы не используем это много.)

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

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

Мы рассматриваем это как стандарт кодирования. Мы не документируем это для каждого звонка.

3 голосов
/ 26 января 2009

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

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

Большая проблема с сохранением ссылок состоит в том, что ваши вызывающие абоненты никогда не будут помнить, что им не разрешено уничтожать эти вещи, и вы в конечном итоге получите «использование удаленного объекта» сбоев типа.

2 голосов
/ 27 января 2009

Я использую следующие префиксы метода для обозначения владения объектом. Право собственности означает ответственность за удаление.

Y.take_ZZZ(x)

Вызывающий передает право владения x на Y, и вызывающий не должен далее обращаться к x. (потому что Y мог даже немедленно удалить x).

Y.own_ZZZ(x)

Похоже взять; вызывающая сторона передает владение x на Y, но вызывающая сторона может продолжать ссылаться на x и ожидать, что Y не будет немедленно удалить х (по крайней мере, до тех пор, пока существует Y, и предполагается, что Y будет существовать, по крайней мере, в контексте знания вызывающим абонентом Y). Обычно Y не удаляет x, пока Y не будет уничтожен (хотя Y может передать право собственности).

Y.know_ZZZ(x)

Вызывающий предоставляет указатель / ссылку на x (вызывающий может сам или не может иметь x), но Y не должен вступать во владение x. Y будет ожидать, что x будет существовать до тех пор, пока он существует.

x = Y.provide_ZZZ()

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

x = Y.give_ZZZ()

Y передает х вызывающему и больше не будет ссылаться на него. В этом случае вызывающая сторона будет владеть x и отвечать за ее удаление или передачу другому объекту.

x = Y.lend_ZZZ()

Y предоставляет ссылку / указатель на x для вызывающей стороны (противоположно know_ZZZ) У сохраняет право собственности. (аналогично предоставлению, но Y не создает экземпляр X, поэтому он либо будет иметь x и вернет ссылку (указатель), либо не получит и вернет null.

x = Y.check_out_ZZZ() and Y.check_in_ZZZ(x)

При оформлении заказа Y предоставляет звонящему эксклюзивный доступ к x до тех пор, пока звонящий не проверит его.

x = Y.create_ZZZ()

Y создаст x и немедленно передаст его вызывающей стороне (заводская функция).

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

2 голосов
/ 26 января 2009

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

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

Я использую константные указатели, чтобы указать, что ввод является побочной ссылкой, и что вызываемый абонент получит копию по ссылке. Продолжительность жизни аргумента должна превышать продолжительность жизни вызываемого. (Дело 2.)

Я использую неконстантные указатели, чтобы указать, что ввод является побочной ссылкой, и что вызываемый абонент будет изменять указатель в случае необходимости. Продолжительность аргумента намеренно не определена в общем случае. (т. е. случай 2 или 3)

Я не использую неконстантные ссылки сам по себе, просто потому, что синтаксически в точке вызова не очевидно, что происходит, но это просто моя личная склонность. У меня есть свои причины, но это не значит, что кто-то еще должен согласиться! Таким образом, можно было бы с пользой использовать различие между указателем и указателем, чтобы указать срок службы, как я описал в случае с const. В этом случае, возможно, указатель = случай 2, а ссылка = случай 3.

Я указываю случай 1 (вызываемый абонент вступает во владение) комментарием; это достаточно редко встречается в моем коде, поэтому я не слишком беспокоюсь об этом.

2 голосов
/ 26 января 2009

Хотя документация является единственным ответом, она, к сожалению, бесполезна. Исследования, которые я проводил, показывают, что многие клиенты никогда не читают документацию методов, которые они используют.

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

В моем собственном инструменте (для Java / Eclipse) у меня есть тег для этого, который позволяет мне сообщать пользователю, когда ему нужно знать что-то важное о параметре.

2 голосов
/ 26 января 2009

Может быть, я упускаю суть вашего вопроса, но если прототипы ваших методов в вашем заголовочном файле настроены правильно, тогда это будет сделано.
«Неявно» пользователь будет знать, что данный метод принимает только ссылку на параметр или данный параметр доступен только для чтения (const) .. и т. Д.

1 голос
/ 26 января 2009

Если вы ищете ясности, документация - это хороший способ справиться с этим. Конечно, это предполагает, что люди обращают внимание на ваши документы.

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

Если вы находитесь в среде, где доступен IntelliSense, убедитесь, что в описании параметра присутствует необходимая информация. Это может спасти вас от любого из этих других шагов.

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