Да, это означает, что часть интерфейса объекта состоит из функций, не являющихся членами.
И вы правы в том, что в нем используется следующая запись для объекта класса T:
void T::doSomething(int value) ; // method
void doSomething(T & t, int value) ; // non-member non-friend function
Если вы хотите, чтобы функция / метод doSomething возвращали void и имели параметр int с именем "value".
Но стоит упомянуть две вещи.
Во-первых, функциональная часть интерфейса класса должна находиться в одном и том же пространстве имен. Это еще одна причина (если нужна была другая причина) использовать пространства имен, только для того, чтобы «соединить» объект и функции, являющиеся частью его интерфейса.
Хорошая часть заключается в том, что он способствует хорошей инкапсуляции. Но плохая часть в том, что он использует функциональную нотацию, которая мне лично не нравится.
Во-вторых, операторы не подпадают под это ограничение. Например, оператор + = для класса T можно записать двумя способами:
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 ;
}
Но обе записи используются как:
void doSomething(T & a, T & b)
{
a += b ;
}
, что с эстетической точки зрения намного лучше, чем функциональная запись.
Теперь, это был бы очень крутой синтаксический сахар, чтобы иметь возможность написать функцию из того же интерфейса и при этом иметь возможность вызывать ее через "." обозначение, как в C #, как упомянуто michalmocny.
Редактировать: Некоторые примеры
Допустим, по какой-то причине я хочу создать два "целочисленных" класса.
Первым будет 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 ;
} ;
Этот класс имеет 6 методов, которые могут получить доступ к его внутренним элементам.
Вторым является 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() ;
}
Он имеет только 3 метода и, таким образом, уменьшает количество кода, который может получить доступ к его внутренним компонентам. Имеет 3 функции, не являющиеся друзьями.
Два класса могут использоваться как:
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 ;
}
}
Когда мы сравниваем использование оператора ("+" и "+ ="), мы видим, что включение оператора в член или не член не имеет различий в его видимом использовании. Тем не менее, есть два различия:
член имеет доступ ко всем своим внутренним элементам. Нечлены должны использовать открытые методы-члены
Из некоторых бинарных операторов, таких как +, *, интересно иметь продвижение типов, потому что в одном случае (например, продвижение lhs, как показано выше) оно не будет работать для метода-члена.
Теперь, если мы сравним неоператорное использование ("toString"), мы увидим, что неоператорское использование является более "естественным" для Java-подобных разработчиков, чем не-членская функция. Несмотря на эту незнакомость, для C ++ важно признать, что, несмотря на его синтаксис, версия, не являющаяся членом, лучше с точки зрения ООП, поскольку она не имеет доступа к внутренним компонентам класса.
В качестве бонуса: если вы хотите добавить оператор (или, соответственно, неоператорную функцию) к объекту, у которого его нет (например, структура GUID ), то вы можете без необходимости изменить саму структуру. Для оператора синтаксис будет естественным, а для неоператора, ну ...
Отказ от ответственности: Конечно, эти классы глупы: set / getValue - это почти прямой доступ к его внутренним компонентам. Но замените Integer на String, как предложено Хербом Саттером в Monoliths «Unstrung» , и вы увидите более реальный случай.