Yes, it means that part of the interface of an object is composed of non member functions.
And you're right about the fact it involves the use of the following notation, for an object of class T:
void T::doSomething(int value) ; // method
void doSomething(T & t, int value) ; // non-member non-friend function
If you want the doSomething function/method return void, and have an int parameter called "value".
But two things are worth mentioning.
The first is that the functions part of the interface of a class should be in the same namespace. This is yet another reason (if another reason was needed) to use namespaces, if only to "put together" an object and the functions that are part of its interface.
The good part is that it promotes good encapsulation. But bad part is that it uses a function-like notation I, personally, dislike a lot.
The second is that operators are not subject to this limitation. For example, the += operator for a class T can be written two ways:
T & operator += (T & lhs, const T & rhs) ;
{
// do something like lhs.value += rhs.value
return lhs ;
}
T & T::operator += (const T & rhs) ;
{
// do something like this->value += rhs.value
return *this ;
}
But both notations are used as:
void doSomething(T & a, T & b)
{
a += b ;
}
which is, from an aesthetic viewpoint, quite better than the function-like notation.
Now, it would be a very cool syntactic sugar to be able to write a function from the same interface, and still be able to call it through the "." notation, like in C#, as mentioned by michalmocny.
Edit: Some examples
Let's say I want, for whatever reason, to create two "Integer-like" classes.
The first will be IntegerMethod:
class IntegerMethod
{
public :
IntegerMethod(const int p_iValue) : m_iValue(p_iValue) {}
int getValue() const { return this->m_iValue ; }
void setValue(const int p_iValue) { this->m_iValue = p_iValue ; }
IntegerMethod & operator += (const IntegerMethod & rhs)
{
this->m_iValue += rhs.getValue() ;
return *this ;
}
IntegerMethod operator + (const IntegerMethod & rhs) const
{
return IntegerMethod (this->m_iValue + rhs.getValue()) ;
}
std::string toString() const
{
std::stringstream oStr ;
oStr << this->m_iValue ;
return oStr.str() ;
}
private :
int m_iValue ;
} ;
This class has 6 methods which can acess its internals.
The second is IntegerFunction:
class IntegerFunction
{
public :
IntegerFunction(const int p_iValue) : m_iValue(p_iValue) {}
int getValue() const { return this->m_iValue ; }
void setValue(const int p_iValue) { this->m_iValue = p_iValue ; }
private :
int m_iValue ;
} ;
IntegerFunction & operator += (IntegerFunction & lhs, const IntegerFunction & rhs)
{
lhs.setValue(lhs.getValue() + rhs.getValue()) ;
return lhs ;
}
IntegerFunction operator + (const IntegerFunction & lhs, const IntegerFunction & rhs)
{
return IntegerFunction(lhs.getValue() + rhs.getValue()) ;
}
std::string toString(const IntegerFunction & p_oInteger)
{
std::stringstream oStr ;
oStr << p_oInteger.getValue() ;
return oStr.str() ;
}
It has only 3 methods, and such, reduces the quantity of code that can access its internals. It has 3 non-member non-friend functions.
The two classes can be used as:
void doSomething()
{
{
IntegerMethod iMethod(25) ;
iMethod += 35 ;
std::cout << "iMethod : " << iMethod.toString() << std::endl ;
IntegerMethod result(0), lhs(10), rhs(20) ;
result = lhs + 20 ;
// result = 10 + rhs ; // WON'T COMPILE
result = 10 + 20 ;
result = lhs + rhs ;
}
{
IntegerFunction iFunction(125) ;
iFunction += 135 ;
std::cout << "iFunction : " << toString(iFunction) << std::endl ;
IntegerFunction result(0), lhs(10), rhs(20) ;
result = lhs + 20 ;
result = 10 + rhs ;
result = 10 + 20 ;
result = lhs + rhs ;
}
}
When we compare the operator use ("+" and "+="), we see that making an operator a member or a non-member has no difference in its apparent use. Still, there are two differences:
the member has access to all its internals. The non-member must use public member methods
From some binary operators, like +, *, it is interesting to have type promotion, because in one case (i.e., the lhs promotion, as seen above), it won't work for a member method.
Now, if we compare the non-operator use ("toString"), we see the member non-operator use is more "natural" for Java-like developers than the non-member function. Despite this unfamiliarity, for C++ it is important to accept that, despite its syntax, the non-member version is better from a OOP viewpoint because it does not have access to the class internals.
As a bonus: If you want to add an operator (resp. a non-operator function) to an object which has none (for example, the GUID structure of <windows.h>), then you can, without needing to modify the structure itself. For the operator, the syntax will be natural, and for the non-operator, well...
Disclaimer: Of course these class are dumb: the set/getValue are almost direct access to its internals. But replace the Integer by a String, as proposed by Herb Sutter in Monoliths "Unstrung", and you'll see a more real-like case.