跳转至

3 Operator Overloading

文本统计:约 973 个字 • 108 行代码

Basic Concepts

Allows user-defined types to act like built-in types

以下运算符都可以被重载

+ - * / % ^ & | ~
= < > += -= *= /= %=
^= &= |= << >> >>= <<= ==
!= <= >= ! && || ++ --
, ->* -> () []
new new[]
delete delete[]

以下运算符不可以被重载

. .* :: ?:
sizeof typeid
static_cast dynamic_cast
const_cast reinterpret_cast

只有存在的运算符可以被重载

  • ** 这样的运算符不可以被重载

被重载的运算符必须符合之前运算符的算子个数并保留之前的优先级


重载运算符的两种方式:

  • 重载运算符可以在类里面作为成员函数进行重载,会有一个隐式的参数

    String String::operator+(const String& that);
    
  • 重载运算符也可以作为全局函数被重载

    String operator+(const String& l, const String& r);
    

Member Function

Integer x(1), y(5), z;
x + y; // x.operator+(y);
  • Implicit first argument

  • Full access to the class definition and all fields

  • No type conversion performed on receiver

对于第三点的意思是接收者不可以做类型转换

z = x + y // Good
z = x + 3 // Good
z = 3 + y // Error

对于二元运算符,相应的重载函数需要一个参数,而对于单元运算符,相应的函数不需要参数

Integer operator-() const { 
 return Integer(-i);
}
...
z = -x; // z.operator=(x.operator-());

Global operators

  • Explicit first argument

  • Does not need special access to classes

  • May need to be a friend

  • Type conversions performed on both arguments

一般来说作为全局函数的运算符重载都会作为某一个类的友元函数

class Integer {
public:
 friend Integer operator+(const Integer&,
 const Integer&); 
 ...
private:
 int i;
};
Integer operator+(const Integer& lhs, const Integer& rhs)
{
 return Integer( lhs.i + rhs.i ); 
}

与之前不同的是,对于两个算子我们都可以做隐式转换

z = x + y; // operator+(x, y)
z = x + 3; // operator+(x, Integer(3))
z = 3 + y; // operator+(Integer(3), y)
z = 3 + 7; // Integer(10)

Argument passing

Pass it as a const reference if it is read-only (except built-ins).

Make member functions const if they do not change the class (relational operators, + , - , etc.).

确认参数是只读的,那么加上 const;对于那些不会改变类中数据的,函数也设置为 const


Return values

返回值的设置与运算符紧密相关,比如说 operator+ 返回的就是一个新的对象,逻辑运算符返回的就是 bool

下面是一些运算符重载的函数签名

  • +-*/%^&|~:T operator X(const T& l, const T& r)

  • ! && || < <= == >= >:bool operator X(const T& l, const T& r)

  • []: E& T::operator [](int index)


Operators ++ and --

如何区分前缀表达式与后缀表达式?

class Integer { 
public: 
 ... 
 Integer& operator++(); //prefix++ 
 Integer operator++(int); //postfix++ 
 Integer& operator--(); //prefix-- 
 Integer operator--(int); //postfix-- 
 ... 
};

对于后缀表达式需要一个 int 参数,然后编译器会自己给他赋值为0

Integer x(5);
++x; // calls x.operator++();
x++; // calls x.operator++(0);
--x; // calls x.operator--();
x--; // calls x.operator--(0);

相应的实现方式如下

Integer& Integer::operator++() { 
 this->i += 1; // increment 
 return *this; // fetch 
} 
// the int argument is not used so leave it unnamed
Integer Integer::operator++( int ){ 
 Integer old( *this ); // fetch 
 ++(*this); // increment 
 return old; // return 
}

注意前缀表达式返回的是一个引用,而后缀表达式返回的是一个对象。同时我们也可以发现后缀是由前缀实现的,因为这两者是相关的。

像这样相关的运算符都可以通过这样的实现方式,因为这样可以保证关联性,方便代码的维护

class Integer { 
public: 
 bool operator==( const Integer& rhs ) const; 
 bool operator!=( const Integer& rhs ) const;
 bool operator<( const Integer& rhs ) const; 
 bool operator>( const Integer& rhs ) const; 
 bool operator<=( const Integer& rhs ) const; 
 bool operator>=( const Integer& rhs ) const; 
}

implement != in terms of ==

bool Integer::operator==( const Integer& rhs ) const { 
 return i == rhs.i; 
} 
bool Integer::operator!=( const Integer& rhs ) const { 
 return !(*this == rhs); 
}

Operator [] and ()

Operator []

  • Must be a member function
  • Single argument
  • Implies that the object acts like an array, should return a reference
// 重载 [] 运算符用于常量对象(只读)
int operator[](int index) const {
    if (index >= size || index < 0) {
        throw out_of_range("Index out of bounds");
    }
    return data[index];
}
Vector v(100); // create a vector of size 100
v[10] = 45;

Operator ()

A functor, which overloads the function call operator, is an object that acts like a function.

struct F {
 void operator()(int x) const {
 std::cout << x << "\n";
 }
}; // F is a functor class
F f; // f is a functor
f(2); // calls f.operator()

User-defined type conversions

Compilers perform implicit conversions using:

  • single-argument constructors 单参数的构造函数

  • type conversion operators 类型转换运算符

Single argument constructors

class PathName { 
 string name; 
public: 
 PathName(const string&); 
 ~ PathName(); 
}; 
... 
string abc("abc"); 
PathName xyz(abc); // OK! 
xyz = abc; // OK abc => PathName

可以直接用 stringPathName 类型初始化,会进行一个隐式的转化。我们也可以在 PathName(const string&); 前面加上 emplicit 使得这样的隐式转换是禁用的

Conversion operator

class Rational { 
public: 
 operator double() const {
 return numerator / (double)denominator;
 }
} 
Rational r(1,3);
double d = 1.3 * r; // r => double
  • The function will be called automatically

  • Return type is same as the function name

这样的转换运算符一般格式为 X::operator T()

  • Operator name is any type descriptor

  • No explicit arguments

  • No return type

A conversion operator can be used to convert an object of one class into an object of

  • another class

  • a built-in type

Summary

Built-in conversions, e.g.,

  • char => short => int => float => double

  • T[] => T*

User-defined type conversions T => C if

  • either C(T) is a valid constructor call for C 这个是在 C 中定义,所以如果 C 为内置类型,那么这种方法就不可以了
  • or operator C() is defined for T

这两者不能同时出现,否则会出现 "模糊" 的错误

注意隐式转换的使用

隐式转换的风险:可能导致程序行为不符合预期,特别是在复杂的代码中,隐式转换可能在不经意间被触发。

显式转换的好处:通过显式声明转换函数,可以更好地控制何时以及如何进行类型转换,从而减少潜在的错误和问题。

评论区

对你有帮助的话请给我个赞和 star => GitHub stars
欢迎跟我探讨!!!