是的,这意味着对象的部分接口由非成员函数组成。
对于 T 类的对象,它涉及使用以下表示法这一事实是正确的:
void T::doSomething(int value) ; // method
void doSomething(T & t, int value) ; // non-member non-friend function
如果你想要 doSomething 函数/方法返回 void,并有一个名为“value”的 int 参数。
但有两件事值得一提。
首先是一个类的接口的函数部分应该在同一个命名空间中。这是使用命名空间的另一个原因(如果需要另一个原因),如果只是为了“组合”一个对象和作为其接口一部分的函数。
好的部分是它促进了良好的封装。但不好的部分是它使用了类似函数的符号,我个人非常不喜欢。
二是运营商不受此限制。例如,类 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++ 来说,重要的是要接受这一点,尽管它有语法,但从 OOP 的角度来看,非成员版本更好,因为它无法访问类内部。
作为奖励:如果您想将一个操作符(或非操作符函数)添加到一个没有操作符的对象(例如,<windows.h> 的 GUID 结构),那么您无需修改结构本身。对于运算符,语法将是自然的,对于非运算符,嗯......
免责声明:这些类当然是愚蠢的:set/getValue 几乎可以直接访问其内部。但是,正如 Herb Sutter 在Monoliths "Unstrung"中提出的那样,将 Integer 替换为字符串,您将看到更真实的案例。