十二、运算符重载
函数重载(Function Overloading)
-
不同的函数有着一样的名字(多态)
-
在C++中,一个函数的表示不仅取决于它的名称,还取决于参数的数量、类型,以及是否带有关键字
const
。示例
cppvoid swap(unsigned long&,unsigned long&); void swap(double&,double&); void swap(char&,char&); void swap(Point&,Point&);
它们都是不同的函数。同名函数有着相似的功能。
为什么运算符需要重载
cpp
class complex(){
public:
complex(double x = 0,double y = 0)
{re = x; im = y;}
const complex Add(const complex& c){
double t1 = re + c.re;
double t2 = im + c.im;
return complex(t1,t2);
}
private:
double re,im;
};
void main(){
complex c,c1,c2(5.5,2);
c = c1.Add(c2);
}
这串代码是没错,但我们希望类之间的+,-可以像内建类型一样,比如:
cpp
int a = 9;int b = 3;
int c = a + b;
我们希望可以将c = c1.Add(c2);
改成 c = c1 + c2;
这里就需要用到操作符重载。
12.1 引言
- 运算符重载是另外一种调用函数的方式。
- 它的目的是让涉及你的类的代码更容易编写,尤其更容易阅读。
- 在表达式中,凡是只包含内建数据类型 的运算符,都是无法被改变的。只有包含用户自定义类型的表达式,才能有重载的运算符。
- 不能定义新的运算符符号,但是当现有运算符集合不够用时,可以使用函数调用的形式来代替。
12.2 语法格式
一般语法格式
-
运算符函数的名称是关键字
operator
加上实际的运算符本身。cpptype operator@(参数列表) { //实现代码 }
-
定义一个重载运算符就像定义一个函数,但是函数的名字是
operator@
,其中@
表示被重载的运算符。 -
运算符重载函数中的参数个数,取决于两个因素:
- 是一元(unary)运算符(一个操作数)还是二元(binary)运算符(两个操作数)。
- 是定义为全局函数还是成员函数:
- 如果是全局函数:
- 一元运算符:一个参数
- 二元运算符:两个参数
- 如果是成员函数:
- 一元运算符:无参数(隐含this)
- 二元运算符:一个参数(另一个操作数)
- 如果是全局函数:
示例:
cpp
//C12:OperatorOverloadingSyntax.cpp
#include <iostream>
using namespace std;
class Integer {
int i;
public:
Integer(int ii) :i(ii) {}
void print() { cout << i << endl; }
const Integer operator+(const Integer& rv) const;
};
const Integer Integer::operator+(const Integer& rv) const {
return Integer(i + rv.i);
}
void main() {
int i = 1, j = 2, k = 0;
k = i + j;
Integer ii(1), jj(2), kk(0);
kk = ii + jj;
kk.print();
}
-
kk = ii + jj;
这一步会先计算右边的ii + jj
,然后正好匹配cppconst Integer operator+(const Integer& rv) const;
因为这是一个成员函数,所以
ii + jj
会被翻译为:cppii.operator+(jj)
也就是说,编译器看到
ii + jj
,就会转化为cppconst Integer temp = ii.operator+(jj);
这时
ii
是隐式的this
,jj
是参数rv
。然后再
cppkk = temp;
所以表达式
cppkk = ii + jj;
等于编译器内部操作
cppconst Integer temp = ii.operator+(jj);//调用operator+ kk = temp;//把结果赋值给kk
成员函数
这里讲述,当运算符是重载函数是成员函数时的语法。
格式:
cpp
type operator@(argument){/*......*/}
- 表示你要重载一个运算符
@
(比如+
,-
,++
等),其返回类型是type
,参数列表根据运算符类型不同而不同。
用法:
-
一元前缀运算符:
- 例子:
@a
- 对应函数:
a.operator@()
- 举例:
-a
会转化为a.operator-()
- 例子:
-
一元后缀运算符:
-
例子:
a@
-
对应函数:
a.operator@(int)
-
说明:后缀运算符需要增加一个
int
类型的占位参数来区分前缀形式(C++为了分辨前缀还是后缀而决定的,有int
是后缀)举例:
a++
会转为a.operator++(int)
,而++a
则是a.operator++()
。
-
-
二元运算符:
- 例子:
a@b
- 对应函数:
a.operator@(b)
- 举例:
a+b
会转为a.operator+(b)
- 例子:
注意:
- 成员函数都会隐式包含一个
this
指针,指向左侧对象a
。 - 前缀一元运算:无参数(如
++a
) - 后缀一元运算:只有一个整型参数
int
(占位符)(如a++
) - 二元运算符:只有一个参数(右操作数),而
this
指向左操作数。
示例
complex.h
cpp
class complex{
public:
complex (double r = 0,double i = 0);
const complex operator+(const complex& c); //二元运算符
const complex operator-();//一元前缀运算符
complex& operator+=(const complex& c);//二元运算符
void print() const;
private:
double real,imag;
};
complex.cpp
cpp
#include <iostream>
#include "complex.h"
using namespace std;
complex::complex(double r, double i) {
real = r; imag = i;
}
const complex complex::operator+(const complex& c) {
double r = real + c.real;
double i = imag + c.imag;
return complex(r, i);
}
const complex complex::operator-() {
return complex(-real, -imag);
}
complex& complex::operator+=(const complex& c) {
real += c.real;
imag += c.imag;
return *this;
}
void complex::print() const {
cout << "(" << real << "," << imag << ")" << endl;
}
user.cpp
cpp
#include <iostream>
#include "complex.h"
void main()
{
complex c1(3.5,5.5),c2(1.5,3.5);
complex c;
c = c1 + c2; // 对应着c = c1.operator(c2)
c.print();
c += c1 += c2; //c1.operator+=(c2);c.operator+=(c1);
c.print();
c = -c1; //c = c1.operator-()
c.print();
}
输出
cpp
(5,9)
(10,18)
(-5,-9)
友元函数
格式:
friend type operator@(argument){/*......*/}
用法:
- 前缀一元运算符:
@a
->operator@(a)
- 后缀一元运算符:
a@
->operator@(a,int)
- 二元运算符:
a@b
->operator@(a,b)
注意:
友元函数和成员函数不同的原因在于,友元函数没有隐形指针。
示例
complex.h
cpp
//complex.h
class complex {
public:
complex(double r = 0, double i = 0);
friend const complex operator+(const complex& c1, const complex& c2); //二元运算符
friend const complex operator-(const complex& c);//一元前缀运算符
friend complex& operator+=(complex& c1,const complex& c2);//二元运算符
void print() const;
private:
double real, imag;
};
complex.cpp
cpp
//complex.cpp
#include <iostream>
#include "complex.h"
using namespace std;
complex::complex(double r, double i) {
real = r; imag = i;
}
const complex operator+(const complex& c1, const complex& c2) {
double r = c1.real + c2.real;
double i = c1.imag + c2.imag;
return complex(r, i);
}
const complex operator-(const complex& c) {
return complex(-c.real, -c.imag);
}
complex& operator+=(complex& c1, const complex& c2) {
c1.real += c2.real;
c1.imag += c2.imag;
return c1;
}
void complex::print() const {
cout << "(" << real << "," << imag << ")" << endl;
}
user.cpp
cpp
//user.cpp
#include <iostream>
#include "complex.h"
void main() {
complex c1(3.5, 5.5), c2(1.5, 3.5);
complex c;
c = c1 + c2; //c = operator(c1,c2)
c.print();
c += c1 += c2; //operator+=(c1,c2);operator+=(c,c1);
c.print();
c = -c1; //operator-(c1)
c.print();
}
输出
cpp
(5,9)
(10,18)
(-5,-9)
运算符重载函数与普通函数
- 运算符函数的声明和调用函数与普通函数相同。
- 运算符函数的参数可以是一个或2个,而普通函数可以有多个参数
- 运算符函数必须是成员函数 ,或者至少有一个参数是用户自定义类型(类或结构)
12.3 可重载的运算符(Overloaded Operators)
12.3.1 一元运算符
- 可重载的一元运算符包括:
+
,-
,!
,~
,++
,--
,&
,*
12.3.2 二元运算符
- 可重载的二元运算符包括:
+
,-
,*
,/
,&
,==
,!=
<
,>
,<=
,>=
,&&
,||
,<<
,>>
=
,+=
,-=
,*=
,/=
,&=
,|=
,^=
,<<=
,>>=
12.3.3 参数与返回值
- 就像处理任何函数参数一样,如果你只需要读取参数 而不修改它,默认应该将其作为常量引用 传递。
- 我们选择哪种类型的返回值,取决于该运算符的预期含义。(例如二元加法运算符
operatoa+
,就应该返回一个常量值------两个操作数的和) - 所有赋值运算符 都会修改左值,因此应该返回一个非常量引用 (便于链式调用)。例如:
a += b
。 - 对于逻辑运算符,通常期望返回的最差情况 是一个
int
,最理想 的是一个bool
。
示例,依旧是12.2当中成员函数的例子,来表现当返回值是const的情况
cpp
//成员函数
const complex complex::operator+(const complex& c){
double r = real + c.real;
double i = imag + c.imag;
return complex(r,i);
}
complex& complex::operator+=(const complex& c){
real = r.real;
imag = c.imag;
return *this;
}
(a+b)按值返回一个const
f(a+b)
:表达式a+b
的结果变成了一个临时的const对象 ,在调用函数f()
时使用这个临时对象。(a+b).g()
:由于a+b
的返回值是const
,这意味着只能调用const成员函数。
示例,来表现临时对象
示例1
cpp
#include <iostream>
using namespace std;
class A{
int i;
public:
A(int x = 0){i = x;cout << "Constructor." << endl;}
A(const A&x){i = x.i ; cout << "Copy Constructor." << endl;}
~A() { cout << "Destructor." << endl; }
};
A f(){return A(1);}
void main(){
f();
cout << "main() End." << endl;
}
输出
cpp
Constructor.
Destructor.
main() End.
示例2
此时我们将示例1中的A f(){return A(1);}
替换成A f(){A a(1);return A(a);}
cpp
#include <iostream>
using namespace std;
class A {
int i;
public:
A(int x = 0) { i = x; cout << "Constructor." << endl; }
A(const A& x) { i = x.i; cout << "Copy Constructor." << endl; }
~A() { cout << "Destructor." << endl; }
};
A f() {
A a(1);
return A(a); // 显式调用拷贝构造
}
void main() {
f(); // 接收返回值,确保 Copy Constructor 被触发(C++14)
cout << "main() End." << endl;
}
输出
cpp
Constructor.
Copy Constructor.
Destructor.
Destructor.
main() End.
解析:
cpp
return A(a)
这一行:
- 创建了一个临时对象(记作
temp
),它是a
的拷贝。 A(a)
是一个 临时的 unnamed 对象 ,将作为函数f()
的返回值。
然后main()
里面调用:
cpp
f();
这个临时返回值没有被捕获,所以:
- 临时对象仍然被创建(必须被创建,因为
return A(a)
是显示拷贝),这里不用return a
,是因能这样可以会被编译器自动优化,导致看不到返回值有临时对象这一过程。 - 它(临时返回值 temp )会在这条语句结束后立即析构。
建议
- 如果运算符的第一个操作数是某个类的对象 ,那么这个运算符应该被声明为该类的成员函数。(例如 a + b 、 a + 5)
- 否做 ,它应该被声明为友元函数。(例如 6 + a)
错误样例
cpp
#include <iostream>
using namespace std;
class complex
{
public:
complex(double r = 0, double i = 0) {
real = r;
imag = i;
cout << "Constructor." << r << "." << i << endl;
}
const complex operator+(const complex& c);
void print() const {
cout << "(" << real << "," << imag << ")" << endl;
}
private:
double real, imag;
};
const complex complex::operator+(const complex& c) {
double r = real + c.real;
double i = imag + c.imag;
return complex(r, i);
}
void main() {
complex c, c1(3.5, 5.5);
c = c1 + 1.5;//ok: c = c1.operator+(1.5)
c.print(); //(5,5.5)
//c = 1.5 + c1; //error: c = 1.5.operator+(c1)
}
输出
cpp
Constructor.0.0 //complex c
Constructor.3.5.5.5 //complex c1(3.5,5.5)
Constructor.1.5.0 // c1.operator.(1.5)中的1.5被隐式转换为complex类型
Constructor.5.5.5 //return complex(r,i),创建了一个新的临时complex(5.5,5.5)
(5,5.5)
正确样例1
cpp
#include <iostream>
using namespace std;
class complex
{
public:
complex(double r = 0, double i = 0) {
real = r;
imag = i;
cout << "Constructor." << r << "." << i << endl;
}
friend const complex operator+(const complex& c1,const complex& c2);
void print() const {
cout << "(" << real << "," << imag << ")" << endl;
}
private:
double real, imag;
};
const complex operator+(const complex& c1,const complex& c2) {
double r = c1.real + c2.real;
double i = c1.imag + c2.imag;
return complex(r, i);
}
void main() {
complex c, c1(3.5, 5.5);
c = c1 + 1.5;//ok: c = operator(c1,1.5)
c.print(); //(5,5.5)
c = 1.5 + c1; //ok: c = operator(1.5,c1)
}
输出
cpp
Constructor.0.0
Constructor.3.5.5.5
Constructor.1.5.0
Constructor.5.5.5
(5,5.5)
Constructor.1.5.0
Constructor.5.5.5
正确样例2
cpp
#include <iostream>
using namespace std;
class X {
public:
void operator+(int) {}
X(int) {}
};
void operator+(X, X) {}
void operator+(X, double) {}
void f(X a)
{
a + 1;//a.operator+(1)
1 + a;//::operator+(X(1),a)
a + 1.0;//::operator+(a,1.0)
}
int main() {
X a(1);
f(a);
return 0;
}
12.3.4特殊运算符
下标运算符[ ]
- 操作符
[]
可以为一个类进行重载。 - 注意:它必须被重载为一个成员函数。
- 因为它的语法必须是
对象[索引]
。
cpp
#include <iostream>
using namespace std;
class vect{
public:
vect(int size){v = new int[size];}
~vect(){delete []v;}
int& operator[](int i);
private:
int* v;
};
int& vect::operator[](int i){
cout << "Subscripting" << endl;
return v[i];
}
int main(){
vect a{5};
a[2] = 12; //a.operator[](2) = 12
cout << a[2] << endl;
return 0;
}
输出
cpp
Subscripting
Subscripting
12
解引用操作符:->
->
运算符必须作为成员函数进行重载。- 该函数应该返回一个指向类的指针。
cpp
#include <iostream>
using namespace std;
struct Student {
int age;
int ID;
};
class X {
public:
X() { S = new Student; S->age = 0; S->ID = 0; }
Student* operator->() { return S; }
~X() { delete S; }
void print() { cout << S->age << "." << S->ID << endl; }
private:
Student* S;
};
int main() {
X x;
x->age = 20;//(x.operator->())->age = 20
x->ID = 001;//(s.operator->())->ID = 001
x.print();
return 0;
}
如果没有
S->age = 0; S->ID = 0;
那么刚开始S里面的age
,ID
会被初始化为垃圾值
输出
cpp
20.1
自增(++)和自减(--)运算符
-
自增(++)和自减(--)运算符可以作为前缀(例如++x)或后缀(例如x++)使用。重载时需要分别定义前缀版本和后缀版本(互相区分的方法是后缀多一个整型的占位符)。通常,这两个运算符重载为类的成员的函数。
-
前缀形式的函数原型:
T operator++()
;后缀形式:T operator++(int)
这里要说明一般来说前缀形式写成
T& operator++()
,包括下面的T& operator--()
,因为这样可以优化掉拷贝函数,并且返回值还可以作为左值(有确切地址的变量)来调用成员函数。 -
前缀形式的函数原型:
T operator--()
;后缀形式:T operator--(int)
-
上述
int
不参与实际使用,仅用于与前缀形式区分后缀形式
cpp
#include<iostream>
using namespace std;
class Increase {
public:
Increase(int val = 0) { value = val; }
Increase(const Increase& i) { cout << "Copy Constructor" << endl; value = i.value; }
void display() const { cout << value << endl; }
Increase operator++(); //前缀
Increase operator++(int); //后缀
private:
int value;
};
Increase Increase::operator++()
{
cout << "++value" << endl;
++value;
return *this;
}
Increase Increase::operator++(int)
{
cout << "value++" << endl;
int temp = value;
value++;
return Increase(temp);
}
void main() {
Increase a(10), b(10), c;
c = ++a; //a.operator++()
c.display();
c = b++; //b.operator++(int)
c.display();
}
输出
cpp
++value
Copy Constructor
11
value++
10
除此之外,还有=
、<<
、>>
下表则展现了函数表达式

12.3.5无法重载的运算符
.
对象成员访问运算符.*
指针到成员运算符::
作用域解析运算符?:
三元条件运算符sizeof
typeid
- 注意:C++中没有内置的指数(幂)运算符;也无法引入新的运算符符号;重载运算符不会改变原有的优先级和结合性规则。
12.4成员还是非成员
-
在重载运算符时,可以选择将其实现为类的成员函数或非成员函数(通常以友元函数形式)。
-
一般来说,当运算符需要访问类的内部实现细节 ,或该运算符本身必须定义为成员(例如,赋值
=
,下标[]
,函数调用()
,以及->
、++
、--
运算符)时,应使用成员函数形式重载。 -
对于对称的二元运算符(如算数运算符、比较运算符等),将其定义为非成员的友元函数 通常更为灵活。这样可以允许运算符左右两侧的操作数进行类型转换。例如,假设类X有一个接受
int
的构造函数:如果operator+
使X的成员函数,那么a + 1
可以执行(调用a.operator+(1)
),但1 + a
将无法匹配;而将operator+
定义为友元函数后,表达式1 + a
可以被转换为operator+(X(1),a)
来执行,使得两种操作数顺序都支持。 -
总而言之,当运算符主要作用于类本身 且不需要左操作数进行额外转换时,倾向于成员函数实现 ;当运算符涉及不同类型之间的交互,或希望支持交换操作数顺序的调用时,使友元函数会更合适。
-
也可以参考下图
12.5重载赋值运算符(Overloading assignment)
-
赋值运算符用于将一个已存在的对象赋值为同类的另一个对象。其重载函数通常声明为:
X& X::operator=(const X& other)
,即返回当前对象的引用,参数为待赋值的同类对象的常量引用。 -
目的:通过重载赋值运算符,可以使已存在的类对象 更新为另一个同类对象的内容。
-
若用户自定义了赋值运算符,则赋值时将调用自定义版本;如果未定义,编译器会尝试生成一个默认的公有赋值运算符(对每个成员执行逐项赋值)。
-
例如:
cppDate t1;//调用构造函数创建 t1 Date t2;//调用拷贝构造函数,用t1初始化t2 Date t3;//调用构造函数创建 t3 t3 = t2;//调用赋值运算符,将t2 赋值给已存在的 t3
示例,用来区分拷贝构造函数和赋值运算符
cpp
#include <iostream>
using namespace std;
class Location {
public:
Location(int xx = 0, int yy = 0) { X = xx; Y = yy; }
Location(const Location& p) {
X = p.X;
Y = p.Y;
cout << "Copy constructor called." << endl;
}
Location& operator=(const Location& p);//赋值运算符
int GetX() { return X; }
int GetY() { return Y; }
private:
int X, Y;
};
Location& Location::operator=(const Location& p) {
X = p.X;
Y = p.Y;
cout << "Assignment operator called." << endl;
return *this;
}
void main() {
Location A(1, 2), B;
B = A;
cout << "B = " << B.GetX() << "," << B.GetY() << endl;
Location C = A;
cout << "C = " << C.GetX() << "," << C.GetY() << endl;
}
输出
cpp
Assignment operator called.
B = 1,2
Copy constructor called.
C = 1,2
-
注意:我们通常需要在赋值运算符中防止自赋值。
cppLocation& Location::operator=(const Location& p) { if (this == &p){ return *this; } X = p.X; Y = p.Y; cout << "Assignment operator called." << endl; return *this; }
-
当我们在重载一个运算符时,我们不能改变:
- 运算符的数量
- 优先级
- 语法结构
- 将运算符组合(比如把
=
和+
组合成+=
)
-
如果类包含指针等动态分配资源 ,默认的浅拷贝赋值会导致多个对象共享同一块内存资源,这可能造成严重错误:如析构时发生双重释放 (多次释放同一内存),或赋值覆盖时产生内存泄漏。
示例1
cppclass A{ int *p; public: A(int i){p = new int(i);} ~A(){delete p;} }; void main(){ A a(5); A b(a); //拷贝函数 A c(6),d(10); d = c; //赋值运算古 }
- 这个示例中,因为是
a.p
和b.p
指向同一内存,5
所在的内存区域会被释放两次,因此报错。 c.p
和d.p
指向一样的存储区域。6
所在的内存区域将会被释放两次(会报错)。10
所在的区域会永远失去。
- 这个示例中,因为是

示例1的修改
cpp
class A{
int *p;
public:
A(int i){p = new int(i);}
~A(){delete p;}
A(const A&r);
A& operator=(const A& r);
};
A::A(const A&r){
p = new int(*r.p);
}
A& A::operator=(const A&r){
if (this == &r) return *this;
delete p; //释放p指向的内存
p = new int(*r.p);
return *this;
}
void main(){
A a(5);
A b(a);
A c(6),d(10);
d = c;
}
这个示例就是正确的

示例2
cpp
#include <iostream>
using namespace std;
class A{
int* p;
public:
A(int i){cout << "Constructor" << endl;p = new int(i);}
~A(){cout << "Destructor" << endl;delete p;}
A (const A& r)//Copy Constructor
{cout << "Copy-constructor" << endl;p = new int(*r.p);}
A& operator=(const A&r);//赋值运算符
void output() {cout << p << "->" << *p << endl;}
};
A& A::operator=(const A&r){
cout << "Assignment" << endl;
if (this == &r) return *this;
delete p;
p = nullptr;
p = new int(*r.p);
return *this;
}
int main(){
A a(5);
A b(a);//拷贝构造函数
A c(6),d(10);
d = c;//赋值运算符
a.output();
b.output();
c.output();
d.output();
return 0;
}
输出
cpp
Constructor
Copy-constructor
Constructor
Constructor
Assignment
000001D298B1C530->5
000001D298B1C870->5
000001D298B1C2F0->6
000001D298B1CCB0->6
Destructor
Destructor
Destructor
Destructor
12.6 自动类型转换(Automatic type conversion)
- 构造函数可以被用来指定类型转换。
- 实现自动类型转换的第二种方法是通过转换运算符重载。
12.6.1 构造函数类型转换
cpp
#include <iostream>
using namespace std;
class complex{
public:
complex(double r = 0,double i = 0);
complex(const complex& a);
friend const complex operator+(const complex& c1,const complex& c2);
private:
double real,imag;
};
complex::complex(double r,double i)
{
real = r;
imag = i;
cout << "constructor:" << real << "," << imag << endl;
}
complex::complex(const complex& a){
real = a.real;
imag = a.imag;
cout << "copy constructor:" << real << "," << imag << endl;
}
const complex operator+(const complex& c1,const complex& c2){
double r = c1.real + c2.real;
double i = c1.imag + c2.imag;
return complex(r,i);
}
void main(){
complex c,c1(3.5,5.5);
c = c1 + 1.5;
}
输出
cpp
constructor:0,0 //c
constructor:3.5,5.5 //c1
constructor:1.5,0 //c = operator+(c1,complex(1.5)),double -> complex
constructor:5,5.5 //return complex(r,i)
限制 :构造函数只能定义从其他类型转换为本类的规则,但无法定义从本类转换为基础类型,或者将本类转换为另一个已有类类型的规则(除非修改那个类的定义来添加相应构造函数)
12.6.2转换运算符
- 为补充构造函数转换的不足,可以在类中定义类型转换运算符 来将类转换为其他类型。其一般形式为:
X::operator type()
。 - 上述声明中,目标类型
type
是运算符名称的一部分,不在函数返回类型位置出现。该运算符函数没有显式参数,它将类 类型转换为指定的目标类型。
cpp
#incldue <iostream>
using namespace std;
class complex
{
double real,imag;
public:
complex(double r = 0,double i = 0)
{
real = r;
imag = i;
cout << "(" << real << "," << imag << ")" << endl;
}
operator int(){return int(real);}
};
void main(){
complex c(3.5,5.5);
int b = d;
cout << b << endl;
cout << int(c) << endl;
}
12.7 重载<<和>>
-
流插入运算符<<和流提取运算符>> 通常重载为友元函数,用于实现自定义类型于输入输出流之间的序列化。
因为友元函数可以访问类的私有成员变量 ,同时
<<
和>>
左边是ostream
或istream
类型(例如cout << "hello world"
,<<
的左边就是ostream
类型),因此要把流运算符重载为友元函数 -
下面示例定义了一个
date
类,并重载<<
和>>
运算符以支持日期的输入和输出。
cpp
#include <iostream>
using namespace std;
class date {
public:
date(int x = 0, int y = 0, int z = 0) { year = x, month = y, day = z; }
friend ostream& operator<<(ostream& ost, const date& d);
friend istream& operator>>(istream& ist, date& d);
private:
int year, month, day;
};
ostream& operator<<(ostream& ost, const date& d) {
//注意ost引用ostream类对象
ost << d.year << ends << d.month << ends << d.day << endl;
return ost;
}
istream& operator>>(istream& ist, date& d) {
//注意ist引用istream类对象
cout << "请输出年月日的数值的数值,如:2025 5 6" << endl;
ist >> d.year >> d.month >> d.day;
return ist;
}
int main() {
date d;
cin >> d; //即调用函数operator>>(cin,d)
cout << d; //operator<<(cout,d)
return 0;
}
12.8 总结
- 运算符函数的命名方式
- 按运算元个数分类:一元、二元、三元运算符
- 可以重载的运算符
- 不可重载的运算符
- 重载实现方式
- 成员函数方式
- 友元函数方式
- 特殊运算符:
[]
、++
、--
、=
、类型转换、<<
、>>
以访问类的私有成员变量 ,同时<<
和>>
左边是ostream
或istream
类型(例如cout << "hello world"
,<<
的左边就是ostream
类型),因此要把流运算符重载为友元函数
- 下面示例定义了一个
date
类,并重载<<
和>>
运算符以支持日期的输入和输出。
cpp
#include <iostream>
using namespace std;
class date {
public:
date(int x = 0, int y = 0, int z = 0) { year = x, month = y, day = z; }
friend ostream& operator<<(ostream& ost, const date& d);
friend istream& operator>>(istream& ist, date& d);
private:
int year, month, day;
};
ostream& operator<<(ostream& ost, const date& d) {
//注意ost引用ostream类对象
ost << d.year << ends << d.month << ends << d.day << endl;
return ost;
}
istream& operator>>(istream& ist, date& d) {
//注意ist引用istream类对象
cout << "请输出年月日的数值的数值,如:2025 5 6" << endl;
ist >> d.year >> d.month >> d.day;
return ist;
}
int main() {
date d;
cin >> d; //即调用函数operator>>(cin,d)
cout << d; //operator<<(cout,d)
return 0;
}
12.8 总结
- 运算符函数的命名方式
- 按运算元个数分类:一元、二元、三元运算符
- 可以重载的运算符
- 不可重载的运算符
- 重载实现方式
- 成员函数方式
- 友元函数方式
- 特殊运算符:
[]
、++
、--
、=
、类型转换、<<
、>>