Выбор между методами экземпляра и бесплатными функциями?
Добавление функциональности в класс может быть выполнено путем добавления метода или путем определения функции, которая принимает объект в качестве первого параметра. Большинство программистов, которые, как я знаю, будут выбирать решение для добавления метода экземпляра.
Однако иногда я предпочитаю создавать отдельную функцию. Например, в примере код ниже Area
и Diagonal
определяются как свободные функции вместо методов. Я считаю, что это лучше, потому что я считаю, что эти функции обеспечивают улучшения, а не основные функции.
Является ли это хорошей/плохой практикой? Если ответ "это зависит", то каковы правила для выбора между добавлением метода или определением отдельной функции?
class Rect
{
public:
Rect(int x, int y, int w, int h) :
mX(x), mY(y), mWidth(w), mHeight(h)
{
}
int x() const { return mX; }
int y() const { return mY; }
int width() const { return mWidth; }
int height() const { return mHeight; }
private:
int mX, mY, mWidth, mHeight;
};
int Area(const Rect & inRect)
{
return inRect.width() * inRect.height();
}
float Diagonal(const Rect & inRect)
{
return std::sqrt(std::pow(static_cast<float>(inRect.width()), 2) + std::pow(static_cast<float>(inRect.height()), 2));
}
Ответы
Ответ 1
GoTW # 84 рассмотрел этот вопрос в отношении std::string
. Он склонялся скорее к противоположной идее: большинство функций должны быть глобальными, если они действительно не должны быть членами.
Я бы сказал, что большинство современных С++ стремится к одной и той же идее. Если вы просматриваете Boost, например, довольно много делается со свободными функциями.
Ответ 2
Если вы хотите использовать его с другими, не связанными классами, он должен быть свободной функцией с переопределениями.
Например, вы можете переопределить float Area( Hexagon const & )
и float Diagonal( Hexagon const & )
. x
, y
, width
и height
однако означают разные вещи для разных фигур.
Абстрактные базовые классы (или несвязанные методы омонимов) - это еще одно решение, но бесплатные функции расширяемы для пользователей, поэтому пользователи эффективно получают более равномерный интерфейс, когда они добавили свои собственные материалы.
Избегайте бесплатных функций с неоднозначными именами, чтобы не связанные функции не становились "конкурирующими" переопределениями.
Ответ 3
Я бы сказал, что это зависит от ваших целей. Если функция является функцией-членом, вы можете использовать ее. и → обозначение с ним, и оно лучше интегрировано с классом. Это также допускает полиморфизм. Если вы хотите, чтобы ваша функция была виртуальной, она должна быть функцией-членом.
Тем не менее, есть школа мысли, что лучше инкапсуляция, чтобы выполнять функции-функции функций только в том случае, если они должны быть. Таким образом, если функции на самом деле не нужен доступ к переменным-членам (по-видимому, потому, что он может делать то, что ему нужно делать только с публичными функциями), тогда вы делаете его отдельной функцией, которая не является членом класса. Таким образом, он не может случайно возиться с внутренними элементами.
Еще одно соображение состоит в том, что, если оно не является функцией-членом, вам нужно беспокоиться о том, могут ли или должны быть применены другие типы к типу, который теперь является первым параметром функции. Иногда это желательно, а иногда и нет. Если это функция-член, то это не проблема (или возможность, в зависимости от того, что вы пытаетесь сделать).
Лично мне не нравятся функции, которые отделены от класса, но вы часто вынуждены писать функции таким образом, когда у вас нет контроля над классом, или вы не можете изменить его по соображениям совместимости или еще что-то.
В самом деле, я думаю, что это сводится к тому, что наиболее важно для вас, и тем, что вы действительно пытаетесь сделать. Я считаю, что наличие функции в составе класса упрощает использование и больше объектно-ориентированных, но лучше обходится, если вы ограничиваете доступ к частным членам вашего класса.