运算符重载的好处:
实现自定义类型之间的运算。
1、运算符重载函数
2、运算符重载函数有两种调用方法
- 显示调用: operator(aa,bb)
- 隐式调用:aa@bb
- ( @为运算符 aa、bb分别为两个不同的参数)
3、友元运算符重载函数
① 友元运算符重载函数的原型
在类的内部声明格式如下:
class X
{
…
friend 返回类型 operator运算符(形参表);
…
}
在类的外部定义格式如下:
返回类型 operator运算符(形参表)
{
函数体
}
②双目友元运算符重载函数(双参数)
例(return 一个 Complex 对象;):
friend CompLex operator+(Complex& a, Complex& b);
③单目友元运算符重载函数(单参数)
例(return 一个 Complex 对象;):
friend Complex operator++(Complex &a);
4、成员运算符重载函数
①成员运算符重载函数的原型
在类的内部定义格式如下:
返回类型 operator运算符(形参表)
{
函数体
}
在类的外部定义格式如下:
先在类内声明:
class X
{
…
返回类型 operator运算符(形参表);
…
}
再在类外定义:
返回类型 X::operator运算符(形参表)
{
函数体
}
②双目成员运算符重载函数(单参数)
例(return 一个 Complex 对象;):
Complex operator+(Complex &a);
③单目成员运算符重载函数(无参数)
例(因为无参数,故return 一个 *this):
Complex operator++();
5、运算符重载时应注意的几个问题
- 不允许用户自己定义新的运算符
- C++中不能重载的运算符只有以下几个:
. 成员访问运算符
.* 成员指针访问运算符
:: 作用域运算符
sizeof 长度运算符
?: 条件运算符
- 最好不要做 将”+”运算符重载为减法操作 这种行为
- 重载不能改变运算符的操作对象(即操作数)的个数
- 重载不能改变运算符原有的优先级
- 重载不能改变运算符原有的结合特性
- 运算符重载函数的参数至少应有一个是类对象(或类对象的引用)
- 双目运算符一般可以被重载为友元(或成员)运算符重载函数,但有一种情况必须使用友元函数。 (如果将一个类Complex对象与一个整数相加:ob2 = 200 + ob1)
6、单目运算符”++”和”–”运算符的重载
①对于友元运算符重载函数
前缀表示:
operator++(Complex &a);
后缀表示:
operator++(Complex &a, int);
②对于成员运算符重载函数
前缀表示:
ob.operator++();
后缀表示:
ob.operator++(int);
这里的int类型参数只是用来区别后缀”++”与前缀”++”,此外没有任何作用。因此在定义函数式也不必使用此参数.C++编译系统在遇到后缀自加自减运算符是,会自动调用相应的函数,参数int一般被传递给值0.
总结
总结:
class Complex
{
public:
// 友元双单目
friend Complex operator+(Complex &a,Complex &b);
friend Complex operator++(Complex &a);
// 成员双单目
Complex operator+(Complex &a);
Complex operator++();
// 友元++,--前后缀
friend Complex operator++(Complex &a);
friend Complex operator++(Complex &a,int);
// 成员++,--前后缀
Complex operator++();
Complex operator++(int);
}
// 友元双单目
Complex operator+(Complex &a,Complex &b);
Complex operator++(Complex &a);
// 成员双单目
Complex::Complex operator+(Complex &a);
Complex::Complex operator++();
// 友元++,--前后缀
Complex operator++(Complex &a);
Complex operator++(Complex &a,int);
// 成员++,--前后缀
Complex::Complex operator++();
Complex::Complex operator++(int);
7、赋值运算符”=”的重载
①浅层拷贝
例:
#include <iostream>
#include <cstring>
using namespace std;
class String
{
public:
String(char *s)
{
cout<<"Constructor called."<<endl;
ptr = new char[strlen(s)+1];
strcpy(ptr,s);
}
~String()
{
cout<<"Desturctor called.---"<<ptr<<endl;
delete []ptr;
}
private:
char *ptr;
};
int main()
{
String p1("book");
String p2("jeep");
p2=p1; // 更改p2,使其指向p1所指地址
return 0;
}
输出结果:
Constructor called.
Constructor called.
Destructor called.—book
Destructor called.—H/g (不同编译器该行 结果不同,本例用的是dev C++5.4.0)
此处最后一行输出错误,原因为执行p2=p1后,p1,p2都指向了字符串book,字符串jeep被封锁起来无法使用,即“指针悬挂”释放p2之后,p1所指的字符串就被随机字符取代了。因此输出有误。
②深层拷贝(解决指针悬挂问题)
在上例中加入如下代码:
类内声明:
String &operator-(const String &);
类外定义:
String &String::operator=(const String &s)
{
if(this==&s) return *this;
delete []ptr;
ptr = new char[strlen(s.ptr)+1];
strcpy(ptr,s.ptr);
return *this;
}
③类的赋值运算符”=”只能重载为成员函数,而不能把它重载为友元函数
8、下标运算符”[]”的重载
①在C++中,在重载下标运算符”[]”时,认为它是一个双目运算符。
例:X[Y] 可看成:
[] —- 双目运算符
X —- 左操作数
Y —- 右操作数
相应的运算符重载函数名为operator[].
②下标运算符重载函数只能定义成员函数,形式如下:
返回类型 类名::operator
{
函数体
}
注意:形参在此表示下标,C++规定只能有一个参数
③使用下表运算符重载函数
例:
#include <iostream>
using namespace std;
class Vector4
{
public:
Vector4(int a1, int a2, int a3, int a4)
{
v[0] = a1;
v[1] = a2;
v[2] = a3;
v[3] = a4;
}
int &operator[](int bi);
private:
int v[4];
};
int &Vector4::operator[](int bi)
{
if(bi<0 || bi>=4)
{
cout<<"Bad subscript!\n";
exit(1);
return v[bi];
}
}
int main()
{
Vector4 v(11,22,33,44);
cout<<v[2]<<endl; // 在此v[2]被解释为v.operator[](2)
v[3]=v[2]; // 在此v[3]被解释为v.operator[](3)
cout<<v[3]<<endl;
v[2]=666;
cout<<v[2];
return 0;
}
运行结果:
33
33
666
9、函数调用运算符”()”的重载
①在C++中,重载函数调用运算符”()”时认为它是一个双目运算符。
例:X(Y)可以看成
() —- 双目运算符
X —- 左操作数
Y —- 右操作数
其相应的运算符函数名为 operator()
②对函数调用运算符重载只能使用成员函数,形式如下:
返回类型 类名::operator()(形参表)
{
函数体
}
③例子 声明一个矩阵类Matrix,在该类中重载运算符”()”,使其能返回矩阵元素的值。
#include <iostream>
using namespace std;
class Matrix
{
public:
Matrix(int,int);
int& operator()(int,int); // 重载运算符"()"
private:
int *m;
int row,col;
};
Matrix::Matrix(int row, int col)
{
this->row = row;
this->col = col;
m = new int[row*col];
for(int i=0;i<row*col;i++)
*(m+i) = i;
}
int& Matrix::operator()(int r,int c)
{
return (*(m+r*col+c)); // 返回矩阵中第r行第c列的值
}
int main()
{
Matrix aM(10,10); // 生成一个矩阵对象aM
cout<<aM(3,4); // 输出矩阵中位于第3行第4列的元素值
aM(3,4)=35; // 改变矩阵中位于第3行第4列的元素值
cout<<endl<<aM(3,4);
return 0;
}
10、重载插入运算符和提取运算符
①重载插入运算符”<<”
“<<”是一个双目运算符,有两个操作数:
左操作数为输出流类ostream的一个流对象(如cout);
右操作数为一个系统预定义的标准类型的常量或变量。
在头文件iostream中有一组运算符函数对运算符”<<”进行重载,
原型为:
ostream& operator<<(ostream& 类型名);
②C++对插入运算符”<<”的功能进行了扩充,可通过重载运算符”<<”实现用户自定义类型的输出。定义的一般格式如下:
ostream& operator<<(ostream& out,自定义类名 &obj)
{
out<<obj.item1;
out<<obj.item2;
...
out<<obj.itemn;
return out;
}
③例 将运算符函数重载为友元函数,用于进行复数的加法运算,并用重载的运算符”<<”输出复数。
#include <iostream>
using namespace std;
class Complex // 声明复数类Complex
{
public:
Complex(double r=0.0, double i=0.0); // 声明构造函数
friend Complex operator+(Complex& a, Complex& b); // 声明运算符"+"重载为友元函数
friend ostream& operator<<(ostream&, Complex&); // 声明运算符"<<"重载为友元函数
private:
double real; // 复数实部
double imag; // 复数虚部
};
Complex::Complex(double r,double i) // 定义构造函数
{
real = r;
imag = i;
}
Complex operator+(Complex &a, Complex &b) // 定义运算符"+"重载函数
{
Complex temp;
temp.real = a.real+b.real;
temp.imag = a.imag+b.imag;
return temp;
}
ostream& operator<<(ostream &out, Complex &com)// 定义运算符"<<"重载函数
{
out<<com.real;
if(com.imag>0) out<<"+";
if(com.imag!=0) out<<com.imag<<"i\n";
return out;
}
int main()
{
Complex com1(2.3,4.6), com2(3.6,2.8), com3; // 定义3个复数类对象
cout<<com1; // 输出复数com1
cout<<com2; // 输出复数com2
com3 = com1+com2; // 复数相加
cout<<com3; // 输出复数相加结果com3
return 0;
}
输出结果:
2.3+4.6i
3.6+2.8i
5.9+7.4i
①重载提取运算符”>>”
“>>”是一个双目运算符,有两个操作数:
左操作数为输入流类istream的一个对象(如cin);
右操作数为一个系统预定义的标准类型的常量或变量。
在头文件iostream中有一组提取运算符函数对运算符”>>”进行重载,
原型为:
istream& operator>>(istream& 类型名&);
②C++对插入运算符”>>”的功能进行了扩充,可通过重载运算符”>>”实现用户自定义类型的输出。定义的一般格式如下:
istream &operator>>(istream& in,自定义类名& obj)
{
in>>obj.item1;
in>>obj.item2;
...
in>>obj.itemn;
return in;
}
③例 将运算符函数重载为友元函数,用于进行负数的加法运算,并用重载运算符”>>”输入复数,用重载的运算符”<<”输出复数。
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(double r,double i)
{
real = r;
imag = i;
}
Complex()
{
real = 0;
imag = 0;
}
friend Complex operator+(Complex,Complex);
friend ostream &operator<<(ostream&,Complex&);
friend istream &operator>>(istream&,Complex&);
private:
double real,imag;
};
Complex operator+(Complex a, Complex b) // 定义重载"+"的运算符函数
{
Complex temp;
temp.real = a.real+b.real;
temp.imag = a.imag+b.imag;
return temp;
}
ostream& operator<<(ostream& output,Complex &obj) // 定义重载"<<"的运算符函数
{
output<<obj.real;
if(obj.imag>0) output<<"+";
if(obj.imag!=0) output<<obj.imag<<"i ";
return output;
}
istream& operator>>(istream& input,Complex &obj) // 定义重载">>"的运算符函数
{
cout<<"请输入复数实部和虚部的值:"<<endl;
input>>obj.real;
input>>obj.imag;
return input;
}
int main()
{
Complex c1(2.4,4.6),c2,c3;
cout<<"复数c1的值是:"<<c1<<endl;
cin>>c2;
cout<<"复数c2的值是:"<<c2<<endl;
c3=c1+c2;
cout<<"复数c3(c3=c1+c2)的值是:"<<c3<<endl;
return 0;
}
运行结果:
复数c1的值是: 2.4+4.6i
请输入复数实部和虚部的值:
3.7 2.5 <—-此处为输入
复数c2的值是: 3.7+2.5i
复数c3(c3=c1+c2)的值是: 6.1+7.1i
11、类型转换
类类型与标准类型间的转换
①通过转换构造函数进行类型转换
②通过类型转换函数进行类型转换
1、通过转换构造函数进行类型转换
( 基本类型–>类类型 或 另一类类型–>本类类型 )
#include <iostream>
using namespace std;
class Coco
{
public:
Coco(int);
void print();
private:
int real;
};
Coco::Coco(int x) // 转换构造函数,将int型数据转换成类Coco的对象
{
this->real = x;
cout<<"调用转换构造函数将 int 型数据 "<<real<<" 转换成类Coco的对象."<<endl;
}
void Coco::print()
{
cout<<"这个对象的数据成员real的值为:"<<real<<endl;
}
int main()
{
Coco x=Coco(4); // ①
x.print();
cout<<"------------------------"<<endl;
Coco y = 6; // ②
y.print();
cout<<"------------------------"<<endl;
y = 9; // ③
y.print();
return 0;
}
①②③的转换都是通过转换构造函数进行的类型转换;
如果没有转换构造函数Coco(int); 语句①②③都是被禁止的。
2、使用转换构造函数将一个指定的数据转换为类对象的方法如下:
i. 先声明一个类
ii. 在这个类中定义一个只有一个参数的构造函数,参数是待转换类型的数据,在函数体中指定转换方法。
iii.用一下形式进行类型转换:
类名(待转换类型的数据)
3、说明:
i. 转换构造函数也是一种构造函数,但是有一个参数的构造函数不一定是转换构造函数。
ii. 转换构造函数不仅可以将一个系统预定义的标准类型数据转换成类的对象,也可以将另一个类的对象转换成转换构造函数所在类的对象。
1、通过类型转换函数进行类型转换
( 类类型 –> 基本类型 )
定义类型转换函数的一般格式为:
operator 目标类型()
{
函数体
}
2、对于格式,其中,
目标类型为希望转换成的类型;
函数名前不能指定返回类型,也不能有参数;
通常函数最后一条语句是return语句,返回值的类型是该函数的目标类型。
3、注意事项
i. 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
ii. 类型转换函数既没有参数,也不能在函数名前指定返回类型。
iii. 类型函数中必须有return语句,即返回目标类型的数据的类型。
iv. 一个类中可以定义多个类型转换函数,C++编译器将自动选择最合适的函数调用。
4、例: 通过类型转换函数进行类型转换
#include <iostream>
using namespace std;
class Coco
{
public:
Coco(double,double); // 声明构造函数
operator double(); // double类型转换函数 将Coco类转换成一个double类型的数据
operator int(); // int类型转换函数 将Coco类转换成一个int类型的数据
void disp();
private:
double x,y;
};
Coco::Coco(double x,double y)
{
this->x = x;
this->y = y;
}
Coco::operator double()
{
return x;
}
Coco::operator int()
{
return int(x);
}
void Coco::disp()
{
cout<<x<<ends<<y<<endl;
}
int main()
{
Coco a(4.4,6.6); // 定义Coco类的对象a
cout<<"Coco类对象a的x和y分别为:"<<endl;
a.disp();
cout<<"Coco类对象a转换成double类型数据为:"<<endl;
cout<<double(a)<<endl; // 调用double类型转换函数,将转换后的double类型的数据显示出来
Coco b(3,8); // 定义Coco类的对象b
cout<<"Coco类对象b的x和y分别为:"<<endl;
b.disp(); // 调用int类型转换函数,将转换后的int类型的数据显示出来
cout<<"Coco类对象b转换成int类型数据为:"<<endl;
cout<<int(b)<<endl;
return 0;
}
5、通过类型转换函数进行隐式类型转换
例:
/* 语言:C++ 编译环境:Dev c++5.4.0 */
#include <iostream>
using namespace std;
class Coco
{
public:
Coco() {} // 无参构造
Coco(int,int); // 双参构造
Coco(int); // 转换构造函数(将int型转换为Coco类的对象)
operator int(); // 类型转换函数(将Coco类的对象转换为int型)
void print(); // 显示结果
private:
int x,y;
};
// 双参构造
Coco::Coco(int x,int y)
{
this->x = x;
this->y = y;
}
// 转换构造函数
Coco::Coco(int k)
{
this->x = this->y = k/2;
}
// 类型转换函数
Coco::operator int()
{
return (this->x+this->y);
}
// 显示结果
void Coco::print()
{
cout<<this->x<<ends<<this->y<<endl;
}
int main()
{
Coco a(1,2),b(3,4);
a.print();
b.print();
Coco c;
c=a+b; // *************
c.print();
return 0;
}
标注处“// ***”的解释如下:
a+b的能运行时由于C++自动进行了隐式转换,该过程步骤如下:
- 寻找能将两个Coco类对象相加的运算符重载函数,未找到。
- 寻找能将Coco类的对象转换成int型数据的类型转换函数operator int(),找到,于是将a,b隐式转换为3和7。
- 寻找能将两个整数相加的运算符函数,这个运算符函数已经在C++中预自定义,于是调用该函数使3和7相加得10.
- 由于语句c=a+b; 的赋值号左边为Coco类的对象,右边为int型10,于是隐式调用转换构造函数将int类型10转换成Coco的一个临时对象。并将这个临时对象的值赋给Coco类对象c,于是c.x=5,c.y-5。
6、整章:此处用重载输入函数复习
程序举例:下面的程序建立了类Triangle,用来存储直角三角形的宽与高。用重载输出运算符函数在屏幕上显示三角形。
/* 语言:C++ 编译环境:Dev-C++5.4.0 */
#include <iostream>
using namespace std;
class Triangle
{
public:
Triangle(int,int);
friend ostream& operator<<(ostream&,Triangle); // 重载输出运算符函数
private:
int x,y;
};
// 构造
Triangle::Triangle(int x,int y)
{
this->x = x;
this->y = y;
}
// 重载输出运算符函数
ostream& operator<<(ostream& stream, Triangle ob)
{
int i,j,h,k;
i = j = ob.x-1;
for(h=ob.y-1; h>0 ;h--) .// 输出第1-(n-1)行
{
for(k=h;k>0;k--) // 输出斜边'*'前的空格
stream<<' ';
stream<<'*'; // 输出'*'
if(j!=i)
{
for(k=j-i-1;k>0;k--)
stream<<' '; // 输出斜边'*'后的空格
stream<<'*'; // 输出'*'
}
i--; // i控制斜边星号前后的空格数
stream<<endl;
}
for(k=0;k<ob.x;k++) // 输出第 n 行
stream<<'*';
stream<<endl;
return stream;
}
int main()
{
Triangle t1(5,5),t2(6,6),t3(12,12);
cout<<t1<<endl;
cout<<t2<<endl;
cout<<t3<<endl;
return 0;
}