14

Adding functionality to a class can be done by adding a method or by defining a function that takes an object as its first parameter. Most programmers that I know would choose for the solution of adding an instance method.

However, I sometimes prefer to create a separate function. For example, in the example code below Area and Diagonal are defined as free functions instead of methods. I find it better this way because I think these functions provide enhancements rather than core functionality.

Is this considered a good/bad practice? If the answer is "it depends", then what are the rules for deciding between adding method or defining a separate function?

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));
}
StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • A side note, using pow with a constant power of two is extremely inefficient relative to just spelling out x*x+y*y+z*z yourself. – Mark B Apr 06 '10 at 19:38
  • 1
    See the classic _[How Non-Member Functions Improve Encapsulation](http://drdobbs.com/cpp/184401197)_. ("When you think encapsulation, you should think non-member functions") Also see http://stackoverflow.com/q/1692084/140719. – sbi Nov 25 '11 at 10:29

3 Answers3

15

GoTW #84 explored this question with respect to std::string. He tended toward rather the opposite idea: most functions should be global unless they really need to be members.

I'd say most modern C++ tends toward the same idea. If you look through Boost, for example, quite a bit is done with free functions.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 4
    I love this quote from Scott Meyers contained in the article: > I'll start with the punchline: If you're writing a function that can be implemented as either a member or as a non-friend non-member, you should prefer to implement it as a non-member function. That decision increases class encapsulation. When you think encapsulation, you should think non-member functions. – Mark Ransom Apr 06 '10 at 18:34
  • 2
    My only probably with this is the namespace pollution. Is float Geometry::Area(const Geometry::Rect& r) better than float Geometry::Rect::Area() ? You don't really want the free function Area to be in the global namespace so you need to put into a namespace. And at that point it looks like a static class function. – jmucchiello Apr 06 '10 at 18:43
  • 2
    @jmucchiello:Yes, most of the free functions should be in namespaces. I don't think of that as making the look like static member functions though -- I'm accustomed to `x::y` meaning "function y in namespace x". – Jerry Coffin Apr 06 '10 at 18:54
  • 3
    @jmucchiello: if you put it in the relevant namespace, then (for better or worse) it will be found by ADL. So you could do `Geometry::Rect r(12,23); std::cout << Area(r);`. – Steve Jessop Apr 06 '10 at 19:01
  • 4
    I got Scott Meyers on my side. I can die now :) – StackedCrooked Apr 06 '10 at 19:35
2

If you want to use it with other, unrelated classes, it should be a free function with overrides.

For example, you could override float Area( Hexagon const & ) and float Diagonal( Hexagon const & ). x, y, width, and height however mean different things for different shapes.

Abstract base classes (or unrelated homonym methods) are another solution, but free functions are user-extensible so users effectively get a more uniform interface once they've added their own stuff.

Avoid free functions with ambiguous names, so that unrelated functions don't become "competing" overrides.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
1

I'd say that it depends on what your goals are. If the function is a member function, then you can use . and -> notation with it, and it's better integrated with the class. It also allows for polymorphism. If you want your function to be virtual, it needs to be a member function.

However, there is a school of thought that it's better encapsulation to make functions member functions only if they need to be. So, if the function doesn't actually need access to member variables (presumably because it can do what it needs to do with just the public functions), then you make it a separate function which is not a member of the class. That way it can't mess with the internals even by accident.

One other consideration is that if it's not a member function, you have to worry about whether other types can or should be cast to the type that is now the first parameter to the function. Sometimes that's desirable and sometimes not. If it's a member function, then that's not a problem (or an opportunity, depending on what you're trying to do).

Personally, I don't like functions which are separate from the class, but you're often forced to write functions that way when you don't have control over the class, or you can't change it for compatability reasons or whatnot.

Really, I think that it comes down to what's most important to you, and what you're really trying to do. I think that having the function be a part of the class makes it easier to use and more object-oriented, but it's better encpasulation if you restrict how much has access to the private members of your class.

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102