十二、操作符重载
十二、运算符重载
函数重载(Function Overloading)
-
不同的函数有着一样的名字(多态)
-
在C++中,一个函数的表示不仅取决于它的名称,还取决于参数的数量、类型,以及是否带有关键字
const
。示例
void swap(unsigned long&,unsigned long&); void swap(double&,double&); void swap(char&,char&); void swap(Point&,Point&);
它们都是不同的函数。同名函数有着相似的功能。
为什么运算符需要重载
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);
}
这串代码是没错,但我们希望类之间的+,-可以像内建类型一样,比如:
int a = 9;int b = 3;
int c = a + b;
我们希望可以将c = c1.Add(c2);
改成 c = c1 + c2;
这里就需要用到操作符重载。
12.1 引言
- 运算符重载是另外一种调用函数的方式。
- 它的目的是让涉及你的类的代码更容易编写,尤其更容易阅读。
- 在表达式中,凡是只包含内建数据类型的运算符,都是无法被改变的。只有包含用户自定义类型的表达式,才能有重载的运算符。
- 不能定义新的运算符符号,但是当现有运算符集合不够用时,可以使用函数调用的形式来代替。
12.2 语法格式
一般语法格式
-
运算符函数的名称是关键字
operator
加上实际的运算符本身。type operator@(参数列表) {//实现代码 }
-
定义一个重载运算符就像定义一个函数,但是函数的名字是
operator@
,其中@
表示被重载的运算符。 -
运算符重载函数中的参数个数,取决于两个因素:
- 是一元(unary)运算符(一个操作数)还是二元(binary)运算符(两个操作数)。
- 是定义为全局函数还是成员函数:
- 如果是全局函数:
- 一元运算符:一个参数
- 二元运算符:两个参数
- 如果是成员函数:
- 一元运算符:无参数(隐含this)
- 二元运算符:一个参数(另一个操作数)
- 如果是全局函数:
示例:
//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
,然后正好匹配const Integer operator+(const Integer& rv) const;
因为这是一个成员函数,所以
ii + jj
会被翻译为:ii.operator+(jj)
也就是说,编译器看到
ii + jj
,就会转化为const Integer temp = ii.operator+(jj);
这时
ii
是隐式的this
,jj
是参数rv
。然后再
kk = temp;
所以表达式
kk = ii + jj;
等于编译器内部操作
const Integer temp = ii.operator+(jj);//调用operator+ kk = temp;//把结果赋值给kk
成员函数
这里讲述,当运算符是重载函数是成员函数时的语法。
格式:
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
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
#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
#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();
}
输出
(5,9)
(10,18)
(-5,-9)
友元函数
格式:
friend type operator@(argument){/*……*/}
用法:
- 前缀一元运算符:
@a
->operator@(a)
- 后缀一元运算符:
a@
->operator@(a,int)
- 二元运算符:
a@b
->operator@(a,b)
注意:
友元函数和成员函数不同的原因在于,友元函数没有隐形指针。
示例
complex.h
//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
//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
//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();
}
输出
(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的情况
//成员函数
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
#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;
}
输出
Constructor.
Destructor.
main() End.
示例2
此时我们将示例1中的A f(){return A(1);}
替换成A f(){A a(1);return A(a);}
#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;}
输出
Constructor.
Copy Constructor.
Destructor.
Destructor.
main() End.
解析:
return A(a)
这一行:
- 创建了一个临时对象(记作
temp
),它是a
的拷贝。 A(a)
是一个 临时的 unnamed 对象 ,将作为函数f()
的返回值。
然后main()
里面调用:
f();
这个临时返回值没有被捕获,所以:
- 临时对象仍然被创建(必须被创建,因为
return A(a)
是显示拷贝),这里不用return a
,是因能这样可以会被编译器自动优化,导致看不到返回值有临时对象这一过程。 - 它(临时返回值 temp )会在这条语句结束后立即析构。
建议
- 如果运算符的第一个操作数是某个类的对象,那么这个运算符应该被声明为该类的成员函数。(例如 a + b 、 a + 5)
- 否做,它应该被声明为友元函数。(例如 6 + a)
错误样例
#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)
}
输出
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
#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)
}
输出
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
#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特殊运算符
下标运算符[ ]
- 操作符
[]
可以为一个类进行重载。 - 注意:它必须被重载为一个成员函数。
- 因为它的语法必须是
对象[索引]
。
#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) = 12cout << a[2] << endl;return 0;
}
输出
Subscripting
Subscripting
12
解引用操作符:->
->
运算符必须作为成员函数进行重载。- 该函数应该返回一个指向类的指针。
#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 = 20x->ID = 001;//(s.operator->())->ID = 001x.print();return 0;
}
如果没有
S->age = 0; S->ID = 0;
那么刚开始S里面的age
,ID
会被初始化为垃圾值
输出
20.1
自增(++)和自减(–)运算符
-
自增(++)和自减(–)运算符可以作为前缀(例如++x)或后缀(例如x++)使用。重载时需要分别定义前缀版本和后缀版本(互相区分的方法是后缀多一个整型的占位符)。通常,这两个运算符重载为类的成员的函数。
-
前缀形式的函数原型:
T operator++()
;后缀形式:T operator++(int)
这里要说明一般来说前缀形式写成
T& operator++()
,包括下面的T& operator--()
,因为这样可以优化掉拷贝函数,并且返回值还可以作为左值(有确切地址的变量)来调用成员函数。 -
前缀形式的函数原型:
T operator--()
;后缀形式:T operator--(int)
-
上述
int
不参与实际使用,仅用于与前缀形式区分后缀形式
#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();
}
输出
++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)
,即返回当前对象的引用,参数为待赋值的同类对象的常量引用。 -
目的:通过重载赋值运算符,可以使已存在的类对象 更新为另一个同类对象的内容。
-
若用户自定义了赋值运算符,则赋值时将调用自定义版本;如果未定义,编译器会尝试生成一个默认的公有赋值运算符(对每个成员执行逐项赋值)。
-
例如:
Date t1;//调用构造函数创建 t1 Date t2;//调用拷贝构造函数,用t1初始化t2 Date t3;//调用构造函数创建 t3 t3 = t2;//调用赋值运算符,将t2 赋值给已存在的 t3
示例,用来区分拷贝构造函数和赋值运算符
#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;
}
输出
Assignment operator called.
B = 1,2
Copy constructor called.
C = 1,2
-
注意:我们通常需要在赋值运算符中防止自赋值。
Location& Location::operator=(const Location& p) {if (this == &p){return *this;}X = p.X;Y = p.Y;cout << "Assignment operator called." << endl;return *this; }
-
当我们在重载一个运算符时,我们不能改变:
- 运算符的数量
- 优先级
- 语法结构
- 将运算符组合(比如把
=
和+
组合成+=
)
-
如果类包含指针等动态分配资源,默认的浅拷贝赋值会导致多个对象共享同一块内存资源,这可能造成严重错误:如析构时发生双重释放(多次释放同一内存),或赋值覆盖时产生内存泄漏。
示例1
class 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的修改
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
#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;
}
输出
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 构造函数类型转换
#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;
}
输出
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
是运算符名称的一部分,不在函数返回类型位置出现。该运算符函数没有显式参数,它将类 类型转换为指定的目标类型。
#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
类,并重载<<
和>>
运算符以支持日期的输入和输出。
#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
类,并重载<<
和>>
运算符以支持日期的输入和输出。
#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 总结
- 运算符函数的命名方式
- 按运算元个数分类:一元、二元、三元运算符
- 可以重载的运算符
- 不可重载的运算符
- 重载实现方式
- 成员函数方式
- 友元函数方式
- 特殊运算符:
[]
、++
、--
、=
、类型转换、<<
、>>