10 Overloaded Operator
1 概念
运算符重载是 C++ 提供的一种功能,允许程序员为用户定义的类型(如类或结构体)重新定义标准运算符的行为
目的:使用户定义的类型能够像内置类型一样使用运算符
限制:不能创建新的运算符,只能重载已有的运算符
运算符重载通过定义一个特殊的成员函数或友元函数实现
成员函数形式:运算符作为类的成员函数时,左操作数必须是类的对象。ReturnType operatorOp(Arguments);
友元(全局)函数形式:运算符作为友元函数时,可以访问类的私有成员,且左操作数不必是类的对象。friend ReturnType operatorOp(Arguments);
可重载的运算符:
算术运算符:+
, -
, *
, /
, %
关系运算符:==
, !=
, <
, >
, <=
, >=
逻辑运算符:&&
, ||
, !
位运算符:&
, |
, ^
, ~
, <<
, >>
赋值运算符:=
, +=
, -=
, *=
, /=
, %=
, <<=
, >>=
, &=
, |=
, ^=
类型转换运算符:Type()
其他运算符:++
, --
, []
, ()
, ->
, ->*
, new
, delete
不可重载的运算符:
::
(作用域解析运算符)
.*
(成员指针访问运算符)
.
(成员访问运算符)
?:
sizeof
(大小运算符)
typeid
(类型信息运算符)
static_cast
, dynamic_cast
等(类型转换)
注意事项:
不能改变运算符的优先级和结合性:运算符的优先级和结合性是固定的,无法通过重载改变
避免滥用运算符重载:运算符重载应符合直觉,避免引入令人困惑的行为
返回值类型:
对于赋值运算符(如 =
),应返回当前对象的引用(*this
)
对于算术运算符(如 +
),通常返回一个新对象
自定义赋值运算符时注意自赋值:在实现赋值运算符时,应检查 this == &other
,以避免自赋值导致的问题
运算符类别
运算符
可重载类型
通常返回类型
备注
算术运算符
+
, -
, *
, /
, %
成员或友元
新对象(如T
或const T
)
通常返回新对象,不修改操作数
+
(正), -
(负)
成员
T
或const T
一元运算符,成员函数无参数
自增/自减
++a
(前置)
成员
T&
返回修改后的对象引用
a++
(后置)
成员
T
(旧值副本)
需添加 int
哑参数区分(如 operator++(int)
)
--a
(前置)
成员
T&
同前置++
a--
(后置)
成员
T
(旧值副本)
需添加 int
哑参数(如 operator--(int)
)
关系运算符
==
, !=
, <
, >
, <=
, >=
成员或友元
bool
通常成对重载(如==
和!=
)
逻辑运算符
&&
, ||
成员或友元
bool
短路行为可能丢失,谨慎使用
!
成员
bool
一元运算符
位运算符
&
, |
, ^
成员或友元
新对象(如T
)
按位操作
~
成员
T
一元运算符(按位取反)
<<
, >>
友元
ostream&
/istream&
输入输出流操作符通常为友元
赋值运算符
=
成员
T&
返回当前对象的引用(支持链式赋值)
+=
, -=
, *=
, /=
, %=
等复合赋值
成员
T&
修改当前对象并返回引用
类型转换运算符
Type()
成员
任意目标类型
无返回类型声明(如 operator int() const
)
其他运算符
[]
成员
T&
或 const T&
需重载const
和非const
版本
()
成员
任意类型
函数对象(仿函数)
->
, ->*
成员
指针或代理对象
用于智能指针或迭代器
new
, delete
成员(静态)
void*
(new
), void
(delete
)
静态成员函数,即使不显式声明
,
(逗号)
成员或友元
任意类型
不推荐重载(易混淆)
&
(取地址)
成员
指针类型
极少需要重载
建议
对称运算符(如 +
, ==
)推荐用全局函数:支持 a + b
和 b + a
的互换性
复合赋值运算符(如 +=
)用成员函数:直接修改对象状态
输入/输出运算符(<<
, >>
)必须全局函数:因为左操作数是流对象
2 运算符重载的实现
2.1 算术运算符
#include <iostream>
using namespace std ;
class Complex {
private :
double real , imag ;
public :
Complex ( double r = 0 , double i = 0 ) : real ( r ), imag ( i ) {}
// 重载加法运算符(成员函数形式)
Complex operator + ( const Complex & other ) const {
return Complex ( real + other . real , imag + other . imag );
}
void display () const {
cout << real << " + " << imag << "i" << endl ;
}
};
int main () {
Complex c1 ( 1.0 , 2.0 ), c2 ( 3.0 , 4.0 );
Complex c3 = c1 + c2 ; // 调用重载的 +
c3 . display ();
return 0 ;
}
2.2 关系运算符
#include <iostream>
using namespace std ;
class Box {
private :
double volume ;
public :
Box ( double v ) : volume ( v ) {}
// 重载小于运算符(友元函数形式)
friend bool operator < ( const Box & b1 , const Box & b2 ) {
return b1 . volume < b2 . volume ;
}
};
int main () {
Box b1 ( 10.0 ), b2 ( 20.0 );
if ( b1 < b2 ) {
cout << "b1 is smaller than b2" << endl ;
}
return 0 ;
}
2.3 输入/输出运算符
输入/输出运算符通常以友元函数形式重载
#include <iostream>
using namespace std ;
class Point {
private :
int x , y ;
public :
Point ( int x = 0 , int y = 0 ) : x ( x ), y ( y ) {}
// 重载输出运算符
friend ostream & operator << ( ostream & os , const Point & p ) {
os << "(" << p . x << ", " << p . y << ")" ;
return os ;
}
// 重载输入运算符
friend istream & operator >> ( istream & is , Point & p ) {
is >> p . x >> p . y ;
return is ;
}
};
int main () {
Point p1 , p2 ;
cout << "Enter coordinates for p1: " ;
cin >> p1 ;
cout << "p1: " << p1 << endl ;
return 0 ;
}
input Enter coordinates for p1 : 3 4
2.4 下标运算符
下标运算符 []
通常以成员函数形式重载
#include <iostream>
using namespace std ;
class Array {
private :
int arr [ 10 ];
public :
Array () {
for ( int i = 0 ; i < 10 ; ++ i ) arr [ i ] = i ;
}
// 重载下标运算符
int & operator []( int index ) {
return arr [ index ];
}
};
int main () {
Array a ;
a [ 2 ] = 42 ; // 使用重载的 []
cout << "a[2] = " << a [ 2 ] << endl ;
return 0 ;
}
2.5 函数调用运算符
函数调用运算符 ()
可以重载,使对象像函数一样调用
#include <iostream>
using namespace std ;
class Multiply {
public :
int operator ()( int a , int b ) {
return a * b ;
}
};
int main () {
Multiply multiply ;
cout << "3 * 4 = " << multiply ( 3 , 4 ) << endl ; // 使用重载的 ()
return 0 ;
}
2.6 特殊运算符
1.赋值运算符 =
class MyClass {
public :
MyClass & operator = ( const MyClass & other ) {
if ( this == & other ) return * this ; // 防止自赋值
// 赋值逻辑
return * this ;
}
};
2.自增/自减运算符 ++
和 --
前置版本 MyClass & operator ++ () {
// 前置自增逻辑
return * this ;
}
后置版本 MyClass operator ++ ( int ) {
MyClass temp = * this ;
// 后置自增逻辑
return temp ;
}
2.7 创建操纵符
manipulator:操纵符。操纵符的本质是一个函数,通常接受一个流对象(如 std::ostream
或 std::istream
)作为参数,并返回该流对象的引用
无参数操纵符:不需要额外的参数,直接影响流的行为。
std::endl
:插入换行符并刷新输出缓冲区
std::flush
:刷新输出缓冲区
std::ws
:跳过输入流中的空白字符
有参数操纵符:需要额外的参数来指定行为,通常通过 #include <iomanip>
提供
std::setw(n)
:设置输出字段的宽度
std::setprecision(n)
:设置浮点数的精度
std::setfill(c)
:设置填充字符
无参数操纵符 #include <iostream>
using namespace std ;
// 自定义操纵符,输出分隔线
ostream & separator ( ostream & os ) {
return os << "--------------------" << endl ;
}
int main () {
cout << "Hello" << separator << "World" << endl ;
return 0 ;
}
output Hello
--------------------
World
2.8 类型转换运算符
函数签名:operator type() const
type
:目标类型(如 int
、double
或用户定义的类型)
类型转换运算符没有返回类型,因为返回类型就是目标类型
将类对象转换为基本数据类型 #include <iostream>
using namespace std ;
class Complex {
private :
double real , imag ;
public :
Complex ( double r , double i ) : real ( r ), imag ( i ) {}
// 重载类型转换运算符,将 Complex 转换为 double
operator double () const {
return real ; // 返回实部
}
};
int main () {
Complex c ( 3.5 , 2.5 );
double realPart = c ; // 隐式调用类型转换运算符
cout << "Real part: " << realPart << endl ; // 输出: Real part: 3.5
return 0 ;
}
将类对象转换为其他类类型 #include <iostream>
using namespace std ;
class Point {
private :
int x , y ;
public :
Point ( int x , int y ) : x ( x ), y ( y ) {}
int getX () const { return x ; }
int getY () const { return y ; }
};
class Polar {
private :
double radius , angle ;
public :
Polar ( double r , double a ) : radius ( r ), angle ( a ) {}
// 重载类型转换运算符,将 Polar 转换为 Point
operator Point () const {
return Point ( static_cast < int > ( radius ), static_cast < int > ( angle ));
}
void display () const {
cout << "Radius: " << radius << ", Angle: " << angle << endl ;
}
};
int main () {
Polar p ( 5.5 , 45.0 );
Point pt = p ; // 隐式调用类型转换运算符
cout << "Point: (" << pt . getX () << ", " << pt . getY () << ")" << endl ; // 输出: Point: (5, 45)
return 0 ;
}
注意事项:
类型转换运算符可以隐式调用,也可以通过显式转换调用(如 static_cast
)
如果不希望隐式调用,可以使用 explicit
关键字修饰类型转换运算符(C++11 引入)
类型转换运算符的返回值必须是目标类型的值,不能返回引用
class MyClass {
public :
explicit operator int () const {
return 42 ;
}
};
int main () {
MyClass obj ;
// int x = obj; // 错误:explicit 禁止隐式转换
int x = static_cast < int > ( obj ); // 正确:显式转换
return 0 ;
}
3 Single Argument Constructors
单参数构造函数(Single Argument Constructor)是指只有一个参数的构造函数。它通常用于将一个类型的值转换为类对象。由于其特殊性,单参数构造函数可能会引发隐式类型转换,因此需要特别注意其用法
当创建类对象时,如果提供了一个与构造函数参数类型匹配的值,编译器会调用该构造函数来初始化对象
3.1 隐式类型转换
#include <iostream>
using namespace std ;
class MyClass {
private :
int value ;
public :
// 单参数构造函数
MyClass ( int v ) : value ( v ) {}
void display () const {
cout << "Value: " << value << endl ;
}
};
int main () {
MyClass obj = 42 ; // 隐式调用单参数构造函数
MyClass obj2 ( 42 ); // 显式调用构造函数
obj2 = 30 ; // 隐式转换
obj . display (); // 输出: Value: 42
return 0 ;
}
在上面的代码中,MyClass obj = 42
会隐式调用单参数构造函数,将 42 转换为 MyClass
类型的对象
obj2 = obj
:
调用构造函数:用 30 创建一个临时对象(这里会发生隐式转换,从 int
→ MyClass
)
调用默认拷贝赋值运算符:将这个临时对象赋值给 obj2
这个对象
3.2 explicit
隐式类型转换可能会导致意外的行为。为了避免这种情况,可以使用 explicit
关键字修饰单参数构造函数(C++11 引入)
#include <iostream>
using namespace std ;
class MyClass {
private :
int value ;
public :
explicit MyClass ( int v ) : value ( v ) {}
void display () const {
cout << "Value: " << value << endl ;
}
};
int main () {
// MyClass obj = 42; // 错误:explicit 禁止隐式转换
MyClass obj ( 42 ); // 正确:显式调用构造函数
// obj2 = 30; // 错误:explicit 禁止隐式转换
obj . display (); // 输出: Value: 42
return 0 ;
}
通过添加 explicit
,只能通过显式调用构造函数来创建对象,从而避免隐式类型转换
C++ 类型转换机制
1.内置类型转换 (Built-in conversions):适用于基本数据类型(primitive types)
char
→ short
→ int
→ float
→ double
char
→ short
→ int
→ long
这些是 C++ 标准定义的隐式类型提升规则
2.隐式类型转换 (Implicit conversions):适用于任何类型 T 的隐式转换:
T
→ T&
T&
→ T
T*
→ void*
T[]
→ T*
T*
→ T[]
T
→ const T
3.用户定义类型转换 (User-defined):两种方式实现用户定义类型转换:
构造函数转换:如果类 C 有接受类型 T
的构造函数 C(T)
转换运算符:如果类 T
定义了 operator C()
4.使用建议 :
一般不建议使用隐式类型转换,因为可能导致函数被意外调用
建议使用显式转换函数
class Rational {
public :
explicit operator double () const ; // C++11 风格显式转换
double toDouble () const ; // 更明确的转换函数
};