2015-02-11 2 views
18

Например, если бы я был на структуру FooSpec:Неправильно ли перегружать функцию, чтобы взять как указатель, так и ссылку?

struct FooSpec { /* lots of data */ }; 

Это плохая практика, чтобы обеспечить как из следующих, как перегруженные функции? Или я должен просто выбрать один и придерживаться его?

Foo *createFoo(const FooSpec *spec) { /* ... */ } 
Foo *createFoo(const FooSpec &spec) { return createFoo(&spec); } 

Возможно, возникли проблемы, вызванные неожиданными проблемами?

+7

У вас не должно быть никаких проблем * tecnhical *, но я определенно был бы озадачен, увидев API, подобный этому. –

+3

Если одна функция просто вызывает другую, какая польза от несовместимого интерфейса? –

+0

@MatteoItalia: Ну, я имею в виду такие вещи, как побуждение человека к ошибке. – Matt

ответ

21

Должен быть только один способ сделать определенную вещь. Это сделает код более понятным для своих пользователей. Таким образом, вы должны выбрать только один, в зависимости от семантики:

  • Если nullptr является значимой ценностью для передачи в createFoo, а затем объявить функцию, чтобы взять указатель.

  • Если nullptr не должно быть передано createFoo, затем объявить параметр ссылку, чтобы компилятор орать на людей, которые пытаются пройти в nullptr.

Теперь пользователи вашего кода отправятся «Ах, я могу передать нуль этой функции, круто!» или «Хорошо, эта функция требует, чтобы фактический объект работал правильно».

Придерживайтесь этого соглашения на протяжении всего проекта, чтобы сделать интерфейс кода более последовательным.

WhozCraig Или, как выразился,

Выберите подходящую модель и сказать клиентам «это ваш интерфейс.»

+0

Можно сказать, что «если объект должен существовать, передайте по ссылке», это означает, что функции не нужно беспокоиться о недопустимых значениях указателей и их тестировании. –

+0

Кроме того, 'nullptr' должен иметь значение для вашей функции. Например: если вы получите 'nullptr' и' throw invalid_argument', это бессмысленно. В этом случае применение ссылки намного лучше. – edmz

3

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

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

Существует также возможность передачи nullptr_t (или NULL указателей для старых таймеров, таких как я) с указателями const. Я не уверен в этом, учитывая, что вы можете перегружать методы своими аргументами, но опять же в контексте изменяющегося API это имеет смысл.


† оно живое !!!

+0

Значит, вы бы сказали, что интерфейс не должен быть спроектирован таким образом с самого начала? – Matt

+0

@Matt Это, вероятно, не должно, но я уверен, что у кого-то в конце концов возникнет веская причина для этого. Знаете ли вы вескую причину? – didierc

+1

Как @WhozCraig помещал это в свой комментарий, мои причины просто избегали ввода дополнительных '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '. Наверное, это не «хорошая» причина. – Matt

4

Я могу придумать одну незначительную полезную причину для этого.

версия Foo* может проверить на нуль, обработать этот случай, и если не использовать ссылочный случай.

Foo& может затем не проверять наличие пустого места.

Это позволяет абонентам сказать: «Я знаю, что это не null, потому что я проверил», сохранив ветвь внутри функции.

Однако это преимущество (сохранение ветки) бессмысленно, как только стоимость метода проходит очень низко.