我有两个关于超载的问题。
1-为什么有时会使重载运算符成为非成员函数?
friend Class operator-(const Class &rhs);
2-有什么区别
Class operator+(const Class &c1, const Class &c2);
和
Class operator+(const Class &rhs);
如果我想添加两个对象C3 = C1 + C2
?
任何帮助表示赞赏...
我有两个关于超载的问题。
1-为什么有时会使重载运算符成为非成员函数?
friend Class operator-(const Class &rhs);
2-有什么区别
Class operator+(const Class &c1, const Class &c2);
和
Class operator+(const Class &rhs);
如果我想添加两个对象C3 = C1 + C2
?
任何帮助表示赞赏...
如果将二元运算符作为成员函数重载,它最终会不对称:左操作数必须是运算符重载的确切类型,但右操作数可以是任何可以转换为正确类型的东西。
如果使用非成员函数重载运算符,则可以转换两个操作数以获得正确的类型。
你的第二点看起来像是同一点的一个具体例子,根本没有任何分开的东西。这是我正在谈论的一个具体示例:
class Integer {
int val;
public:
Integer(int i) : val(i) {}
operator int() { return val; }
// Integer operator+(Integer const &other) const { return Integer(val + other.val); }
friend Integer operator+(Integer const &a, Integer const &b) {
return Integer(a.val + b.val);
}
};
int main() {
Integer x(1);
Integer y = x + 2; // works with either operator overload because x is already an Integer
Integer z = 2 + x; // works with global overload, but not member overload because 2 isn't an Integer, but can be converted to an Integer.
return 0;
}
另请注意,即使friend
函数的定义在 的类定义中Integer
,它被声明为友元这一事实意味着它不是成员函数——将其声明为friend
使其成为全局函数,而不是成员。
底线:这种重载通常应该作为自由函数而不是成员函数来完成。为用户提供正确(极大地)工作的运算符比“更面向对象”之类的理论考虑更重要。必要时,比如当操作符的实现需要是虚拟的时,你可以做一个两步的版本,你提供一个(可能是虚拟的)成员函数来做真正的工作,但重载本身是一个自由函数,在其左操作数上调用该成员函数。一个相当常见的例子是operator<<
层次结构的重载:
class base {
int x;
public:
std::ostream &write(std::ostream &os) const {
return os << x;
}
};
class derived : public base {
int y;
public:
std::ostream &write(std::ostream &os) const {
return (base::write(os) << y);
}
};
std::ostream &operator<<(std::ostream &os, base const &b) {
return b.write(os);
}
这支持多态实现(以及访问基类的受保护成员,如有必要),而不会放弃操作符的正常特性来获取它。
将二元运算符重载为自由函数的主要例外是赋值运算符(operator=
、operator+=
、operator-=
等operator*=
)。转换会产生一个临时对象,无论如何您都不能分配给它,因此对于这种特殊情况,重载应该(实际上必须是)一个成员函数。
1-为什么有时会使重载运算符成为非成员函数?
这是选择的问题。您可以将其operator +/-
设为类成员或使其成为免费friend
函数。
如果它是一个自由函数,您提供的语法operator -
是错误的friend
,它应该采用 2 个参数(类似于operator +
您的示例)。
2-有什么区别
如前所述,它只是语法不同。第一个应该是自由friend
函数,第二个是class
成员。
最重要的是,我相信将运算符保留为class
成员方法比自由函数要好,因为:
protected
基类的成员,但friend
函数不能让重载的运算符成为朋友是一个更好的选择,因为以这个例子为例
class Comples
{
public:
Complex(float real, float imaginary);
friend operator+ (const Complex &rhs);
...
};
通过让操作员成为朋友,您可以执行以下操作
Complex result1 = 5.0 + Comples(3.0, 2.0);
和
Complex result1 = Comples(3.0, 2.0) + 5.0;
遵守加法社区属性的规则。如果重载的运算符是成员函数,则无法实现这一点。这是由于编译器能够Complex
在这种情况下根据需要隐式创建对象。因此,加法运算符作为朋友时的行为符合数学中的正常加法概念。