Да, это означает, что часть интерфейса объекта состоит из функций, не являющихся членами.
И вы правы в том, что для объекта класса 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 ;
}
что с эстетической точки зрения намного лучше, чем функциональное обозначение.
Теперь было бы очень круто синтаксическим сахаром иметь возможность писать функцию из того же интерфейса и по-прежнему иметь возможность вызывать ее через . обозначение, как в С#, как упоминалось michalmocny.
Изменить: некоторые примеры
Допустим, я хочу по какой-то причине создать два класса, подобных Integer. Первым будет 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 ‹windows.h›), вы можете это сделать, не изменяя сама структура. Для оператора синтаксис будет естественным, а для неоператора ну...
Отказ от ответственности: Конечно, эти классы тупые: set/getValue — это почти прямой доступ к его внутренностям. Но замените целое число строкой, как предложил Херб Саттер в Monoliths Unstrung, и вы увидите более реалистичный случай.
person
paercebal
schedule
01.12.2008