规范运算符重载? 在C++类数类中实现算术运算符重载有一个规范或推荐的模式? 从C++ FAQ中,我们得到了一个避免异常问题的异常安全分配算子: class NumberImpl; class Number { NumberImpl *Impl; ... }; Number& Number::operator=(const Number &rhs) { NumberImpl* tmp = new NumberImpl(*rhs.Impl); delete Impl; Impl = tmp; return *this; }

规范运算符重载? 在C++类数类中实现算术运算符重载有一个规范或推荐的模式? 从C++ FAQ中,我们得到了一个避免异常问题的异常安全分配算子: class NumberImpl; class Number { NumberImpl *Impl; ... }; Number& Number::operator=(const Number &rhs) { NumberImpl* tmp = new NumberImpl(*rhs.Impl); delete Impl; Impl = tmp; return *this; },c++,operator-overloading,C++,Operator Overloading,但是对于其他操作符(+、+=,等等),除了让它们像内置类型上的操作符一样工作之外,很少给出建议 有没有标准的方法来定义这些?这就是我想到的——有没有我没有看到的陷阱 // Member operator Number& Number::operator+= (const Number &rhs) { Impl->Value += rhs.Impl->Value; // Obviously this is more complicated return

但是对于其他操作符(+、+=,等等),除了让它们像内置类型上的操作符一样工作之外,很少给出建议

有没有标准的方法来定义这些?这就是我想到的——有没有我没有看到的陷阱

// Member operator
Number& Number::operator+= (const Number &rhs)
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number lhs, const Number &rhs)
{
     return lhs += rhs;
}

在Bjarne Stroustrup的书“第11章(专门讨论运算符重载的那一章)中,他讲述了为复数类型编写类的过程(第11.3节)

从那部分我注意到的一件事是,他实现了混合类型的操作。。。这可能适用于任何数值类


一般来说,你得到的看起来不错。

惯例是编写
操作符+(常数T&)
操作符-(常数T&)
,根据
操作符+=(常数T&)
操作符-=(常数T&)
。如果向基元类型添加和从基元类型中减去是有意义的,那么您应该编写一个构造函数,从基元类型构造对象。然后,重载运算符也将适用于基元类型,因为编译器将隐式调用相应的构造函数

正如您所提到的,您应该避免授予不需要的函数访问权限。但是在上面的
operator+(Number,const Number&)
代码中,我个人会将两个参数都设置为const引用,并使用一个temp。我认为你的问题下面的评论者忽略了这一点并不奇怪;除非你有很好的理由不这样做,否则要避免意外和诡计,并尽可能明显地表现出来


如果希望代码与其他数字类型集成,例如
std::complex
,请注意循环转换。也就是说,如果
OtherNumeric
提供了一个接受
Numeric
参数的构造函数,则在
Numeric
中不要提供
运算符OtherNumeric()

传统上,根据运算符=X来编写运算符X 传统上,标准运算符的所有参数都是常量

// Member operator
// This was OK
Number& Number::operator+= (Number const& rhs) 
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number const& lhs,Number const& rhs)
{
     // This I would set the lhs side to const.
     // Make a copy into result.
     // Then use += add the rhs
     Number result(lhs);
     return result += rhs;
}
您提到了赋值运算符。

但是你没有提到复制构造函数。因为您的类拥有原始指针的所有权,所以我希望您也定义它。赋值运算符传统上是按照复制构造函数编写的。

< p>编写任何运算符时要考虑的最大问题是成员运算符不在左参数上进行转换:

struct example {
  example(int);
  example operator + (example);
};

void foo() {
  example e(3), f(6);
  e + 4; // okay: right operand is implicitly converted to example
  e + f; // okay: no conversions needed.
  6 + e; // BAD: no matching call.
}
这是因为转换从不适用于成员函数的
This
,这也扩展到了运算符。如果操作符改为全局名称空间中的
example operator+(example,example)
,它将被编译(或者如果使用了pass by const ref)


因此,像
+
-
这样的对称运算符通常被实现为非成员,而像
+=
-=
这样的复合赋值运算符被实现为成员(它们也会更改数据,这意味着它们应该是成员)。而且,由于您希望避免代码重复,对称运算符可以根据复合赋值运算符来实现(如您的代码示例中所示,尽管惯例建议在函数内部创建临时运算符)。

在“运算符+”中,您应该先创建一个副本,然后对其应用“+=”,而不是应用“+=”原始(修改)然后复制,除非我遗漏了什么。这就是为什么lhs被定义为传递值而不是引用。别忘了检查分配给self@OJ:对self的分配是由代码正确处理的(尽管速度很慢)。不要检查self-Assignment。当前的代码是有效的,而且由于自分配很少,额外的测试是悲观的。我看不出使用运算符+(Number const&,Number const&)然后使用临时命令只会增加击键之外的任何内容——这两种方法都会使用相同数量的复制操作(几乎肯定会被优化掉),两者都向调用者明确了参数的变化。目标代码将是相同的,但这只是避免偏离约定的问题。你可以接受这个签名,但是你也可以通过遵循约定来避免一个问题。关于原始指针持有类型的复制构造函数,这是一个很好的观点。但是我不明白,如果你把操作符+()的lhs参数声明为“Number const&”,然后使用一个临时的…@j_random_hacker:如果你不声明它为const,那么你将修改原始值。x=a+b;我不希望“a”的值改变。“foo”的第二行应该是“e+4”而不是“4+e”。为清晰起见进行了编辑(并添加了一些注释以超过6个字符的最小编辑长度!)。哦,除非只有在同行评审之前我才能看到。伟大的