Вы уже получили много противоречивых ответов, так что вам, несомненно, нужен еще один, который противоречит почти всем из них.
С точки зрения эффективности, это вряд ли что-то изменит.Функция, которая просто возвращает значение, несомненно, будет сгенерирована inline, если вы специально не запретите это, отключив all оптимизацию.
Это оставляет только вопрос о том, что предпочтительнее с точки зрения дизайна.По крайней мере, IMO, это обычно , предпочтительнее, чем , а не , в первую очередь get_n
.Как только вы удалите эту проблему с дизайном, вопрос, который вы задали, просто исчезает: поскольку get_n
для начала не существует, вы не можете написать другой код, зависящий от него.
все еще оставляет небольшой вопрос о том, как вы должны делать вещи, хотя.Это (конечно) приводит к большему количеству вопросов.В частности, что представляет собой n
?Я понимаю, что вы, вероятно, приводите гипотетический пример, но хороший дизайн (в данном случае) зависит от знания немного больше о том, что такое n
и как оно используется, а также о типе n
членаи как it также предполагается использовать.
Если n
является членом листового класса, от которого вы не ожидаете деривации, то вам, вероятно, следует использовать другафункция, которая записывает n
напрямую:
class whatever {
int n;
friend std::ostream &operator<<(std::ostream &os, whatever const &w) {
return os << w.n;
}
};
Простой, простой и эффективный.
Если, однако, n
является членом того, что вы ожидаете использовать (или бытьиспользуется) в качестве базового класса, тогда вы обычно хотите использовать «виртуальную виртуальную» функцию:
class whatever {
int n;
virtual std::ostream &write(std::ostream &os) {
return os << n;
}
friend std::ostream &operator<<(std::ostream &os, whatever const &w) {
return w.write(os);
}
};
Обратите внимание, однако, что это предполагает, что вы заинтересованы в выписывании всего объекта, и это простобывает, что, по крайней мере, в текущей реализации это означает выписывание значения n
.
Что касается того, почему вы должны так поступать, есть несколько простых принципов, которые, я думаю, следует соблюдать:
- Либо сделайте что-нибудь действительно приватным, либо сделайте его публичным.Закрытый член с открытым
get_n
(и, как правило, с открытым set_n
также) может потребоваться для JavaBeans (для одного примера), но все еще является действительно плохой идеей и демонстрирует грубое неправильное понимание ориентации объекта или инкапсуляции, не говоря уже о создании совершенно некрасивого кода. - Скажите, не спрашивайте.Публичный
get_n
часто означает, что вы в конечном итоге получаете клиентский код, который выполняет цикл чтения / изменения / записи, а объект выступает в роли тупого контейнера данных.Как правило, предпочтительно преобразовать это в одну операцию, в которой клиентский код описывает желаемый результат, а сам объект выполняет чтение / изменение / запись для достижения этого результата. - Минимизируйте интерфейс.Вы должны стремиться к тому, чтобы каждый объект имел наименьший возможный интерфейс, не причиняя чрезмерной боли пользователям.Исключение публичной функции, такой как
get_n
, почти всегда само по себе хорошо, независимо от того, хороша ли она для инкапсуляции.
Поскольку другие прокомментировали функции friend
, я добавлю свои двецентов стоит на эту тему, а также.Довольно часто можно услышать комментарии о том, что «друга следует избегать, потому что он нарушает инкапсуляцию».
Я должен категорически не согласиться и далее полагать, что любой, кто думает, что ему еще есть над чем научиться думатькак программист.Программист должен мыслить в терминах абстракций, а затем реализовывать эти абстракции настолько разумно, насколько это возможно в реальном мире.
Если объект поддерживает ввод и / или вывод, то вход и выход являются частямиинтерфейс этого объекта , и все, что реализует этот интерфейс, является частью объекта . only другая возможность состоит в том, что тип объекта не не поддерживает ввод и / или вывод.
Суть здесь довольно проста: по крайней мере, для поддержки нормальных соглашений, C ++-вставки и экстракторы должны быть написаны как свободные (не входящие) функции. Несмотря на это, вставка и извлечение являются такой же частью интерфейса класса, как и любые другие операции, и (следовательно) средство вставки / извлечения является такой же частью класса (как абстракция), как и все остальное.
Я хотел бы отметить для записи, что это часть того, почему я предпочитаю реализовывать функции-друзья, задействованные внутри класса, как я показал их выше. С логической точки зрения, они являются частью класса, поэтому хорошо, чтобы они выглядели как часть класса.
Я еще раз повторю для акцента: предоставление им доступа к внутренним объектам класса не может нарушить инкапсуляцию, потому что в действительности они являются частями класса - и странное требование C ++, чтобы они были реализованы как свободные функции, делает не меняйте этот факт на одну-единственную йоту.