C++运算符重载为成员函数和非成员函数的限制

我们知道,基本数据类型定义包括:

值域或需要的内存空间

② 位模式与编码方案;

③ 可以适用的操作(能够使用的运算符);

例如double类型,使用8个字节的内存空间,使用IEEE754的编译方案(1个符号位,11个指数位使用移码,52个小数位),double可以使用+-*/等运算符,但不能使用模运算符%。

一个操作符的运算可以理解为一个函数形式:

a+b;operator+(a,b);

自然,如果类类型要使用运算符,需要明确定义操作符操作数据成员的逻辑(也就是重载操作符),否则,会出现编译错误。

需要注意的是,运算符(operator)重载要求必须至少有一个类类型参数(或两个类类型参数),如果参数一个都不是类类型,则也就没有必要重载。对于第一个操作数(operand,出现在操作符的左边)的类型也是有限制的。

例如运算符[]要求第一个操作数只能是类对象,所以需要重载[]为成员函数(其第一个参数是隐含的this指针,指向类对象)。

运算符<<要求第一个操作数为std::ostream类型对象,所以不能重载为成员函数,可以重载为友元函数。

1 重载双目运算符[]为成员函数

双目运算符[]限制了第一个参数必须为类类型,所以要重载为成员函数(第一个参数为类类型指针this)。

#include class IntList{private: int m_list[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // give this class some initial state for this examplepublic: int& operator[] (int index); int operator[] (int index) const; // could also return const int& // if returning a non-fundamental type, use as a const argument};int& IntList::operator[] (int index) // for non-const objects: can be used for assignment{ return m_list[index];}int IntList::operator[] (int index) const // for const objects: can only be used for access{ return m_list[index];}int main(){ IntList list{}; list[2] = 3; // okay: calls non-const version of operator[] std::cout << list[2] << ''; const IntList clist{}; clist[2] = 3; // compile error: calls const version of operator[], which returns by value. Cannot assign to this because it is an rvalue. std::cout << clist[2] << ''; return 0;}

2 重载双目运算符<<为友元函数

双目运算符<<的第一个参数必须为std::ostream类型对象,为避免第一个对象为this指针,所以要避免重载为成员函数,可以重载为友元函数。

#include class Point{private: double m_x {}, m_y {}, m_z {};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x { x }, m_y { y }, m_z { z } { } friend std::ostream& operator<< (std::ostream &out, const Point &point);};std::ostream& operator<< (std::ostream &out, const Point &point){ // Since operator<< is a friend of the Point class, we can access Point's members directly. out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ")"; return out;}int main(){ Point point1 { 2.0, 3.0, 4.0 }; std::cout << point1; return 0;}

3 重载单目运算符++、–为成员函数

class Digit{private: int m_digit;public: Digit(int digit=0) : m_digit{digit} { } Digit& operator++(); // prefix has no parameter Digit& operator–(); // prefix has no parameter Digit operator++(int); // postfix has an int parameter Digit operator–(int); // postfix has an int parameter friend std::ostream& operator<< (std::ostream& out, const Digit& d);};// No parameter means this is prefix operator++Digit& Digit::operator++(){ // If our number is already at 9, wrap around to 0 if (m_digit == 9) m_digit = 0; // otherwise just increment to next number else ++m_digit; return *this;}// No parameter means this is prefix operator–Digit& Digit::operator–(){ // If our number is already at 0, wrap around to 9 if (m_digit == 0) m_digit = 9; // otherwise just decrement to next number else –m_digit; return *this;}// int parameter means this is postfix operator++Digit Digit::operator++(int){ // Create a temporary variable with our current digit Digit temp{*this}; // Use prefix operator to increment this digit ++(*this); // apply operator // return temporary result return temp; // return saved state}// int parameter means this is postfix operator–Digit Digit::operator–(int){ // Create a temporary variable with our current digit Digit temp{*this}; // Use prefix operator to decrement this digit –(*this); // apply operator // return temporary result return temp; // return saved state}std::ostream& operator<< (std::ostream& out, const Digit& d){out << d.m_digit;return out;}int main(){ Digit digit(5); std::cout << digit; std::cout << ++digit; // calls Digit::operator++(); std::cout << digit++; // calls Digit::operator++(int); std::cout << digit; std::cout << –digit; // calls Digit::operator–(); std::cout << digit–; // calls Digit::operator–(int); std::cout << digit; return 0;}

4 重载函数调用符()为成员函数

#include class Accumulator{private: int m_counter{ 0 };public: int operator() (int i) { return (m_counter += i); }};int main(){ Accumulator acc{}; std::cout << acc(10) << ''; // prints 10 std::cout << acc(20) << ''; // prints 30 return 0;}

You may wonder why we couldn’t do the same thing with a normal function and a static local variable to preserve data between function calls. We could, but because functions only have one global instance, we’d be limited to using it for one thing at a time. With functors, we can instantiate as many separate functor objects as we need and use them all simultaneously.

您可能想知道为什么我们不能用普通函数和静态局部变量来保存函数调用之间的数据。我们可以,但因为函数只有一个全局实例,所以我们只能一次使用一个实例。使用函数对象,我们可以根据需要实例化任意多个单独的函数对象,并同时使用它们。

Operator() is sometimes overloaded with two parameters to index multidimensional arrays, or to retrieve a subset of a one dimensional array (with the two parameters defining the subset to return). Anything else is probably better written as a member function with a more descriptive name.

操作符()有时会重载两个参数来索引多维数组,或检索一维数组的子集(两个参数定义要返回的子集)。其他任何内容都最好用更具描述性的名称作为成员函数编写。

5 重载赋值运算符=为成员函数

MyString& MyString::operator= (const MyString& str){// self-assignment checkif (this == &str)return *this;// if data exists in the current string, delete itif (m_data) delete[] m_data;m_length = str.m_length;// copy the data from str to the implicit objectm_data = new char[str.m_length];for (int i { 0 }; i < str.m_length; ++i)m_data[i] = str.m_data[i];// return the existing object so we can chain this operatorreturn *this;}

6 When to use a normal, friend, or member function overload

何时使用普通、友元或成员函数重载

In most cases, the language leaves it up to you to determine whether you want to use the normal/friend or member function version of the overload. However, one of the two is usually a better choice than the other.

在大多数情况下,该语言将由您决定是否要使用重载的普通/友元或成员函数版本。然而,两者中的一个通常比另一个更好。

When dealing with binary operators that don’t modify the left operand (e.g. operator+), the normal or friend function version is typically preferred, because it works for all parameter types (even when the left operand isn’t a class object, or is a class that is not modifiable). The normal or friend function version has the added benefit of “symmetry”, as all operands become explicit parameters (instead of the left operand becoming *this and the right operand becoming an explicit parameter).

处理不修改左操作数的二元运算符(例如运算符+)时,通常首选普通或友元函数版本,因为它适用于所有参数类型(即使左操作数不是类对象,或是不可修改的类)。普通或友元函数版本具有“对称”的额外好处,因为所有操作数都成为显式参数(而不是左操作数变成*this,右操作数变成显式参数)。

When dealing with binary operators that do modify the left operand (e.g. operator+=), the member function version is typically preferred. In these cases, the leftmost operand will always be a class type, and having the object being modified become the one pointed to by *this is natural. Because the rightmost operand becomes an explicit parameter, there’s no confusion over who is getting modified and who is getting evaluated.

处理确实修改左操作数的二进制运算符(例如运算符+=)时,通常首选成员函数版本。在这些情况下,最左边的操作数将始终是类类型,并且将要修改的对象变为*所指的对象是很自然的。因为最右边的操作数成为显式参数,所以对于谁要修改谁来求值没有任何混淆。

Unary operators are usually overloaded as member functions as well, since the member version has no parameters.

一元运算符通常也作为成员函数重载,因为成员版本没有参数。

The following rules of thumb can help you determine which form is best for a given situation:

以下经验法则可以帮助您确定哪种形式最适合特定情况:

If you’re overloading assignment (=), subscript ([]), function call (()), or member selection (->), do so as a member function.

如果要重载赋值(=)、下标([])、函数调用(())或成员选择(->),请作为成员函数进行重载。

If you’re overloading a unary operator, do so as a member function.

如果要重载一元运算符,请作为成员函数进行重载。

If you’re overloading a binary operator that does not modify its left operand (e.g. operator+), do so as a normal function (preferred) or friend function.

如果要重载不修改其左操作数的二元运算符(例如运算符+),请将其作为普通函数(首选)或友元函数进行重载。

If you’re overloading a binary operator that modifies its left operand, but you can’t add members to the class definition of the left operand (e.g. operator<<, which has a left operand of type ostream), do so as a normal function (preferred) or friend function.

如果要重载修改其左操作数的二元运算符,但不能将成员添加到左操作数的类定义中(例如运算符<<,其左操作数类型为ostream),请将其作为普通函数(首选)或友元函数。

If you’re overloading a binary operator that modifies its left operand (e.g. operator+=), and you can modify the definition of the left operand, do so as a member function.

如果要重载修改其左操作数的二元运算符(例如运算符+=),并且可以修改左操作数的定义,请作为成员函数进行修改。

-End-

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月16日
下一篇 2022年6月16日

相关推荐

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息