Выбор между методами экземпляра и бесплатными функциями?

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

Однако иногда я предпочитаю создавать отдельную функцию. Например, в примере код ниже 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

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

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

Еще одно соображение состоит в том, что, если оно не является функцией-членом, вам нужно беспокоиться о том, могут ли или должны быть применены другие типы к типу, который теперь является первым параметром функции. Иногда это желательно, а иногда и нет. Если это функция-член, то это не проблема (или возможность, в зависимости от того, что вы пытаетесь сделать).

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

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