1. 运算符重载的基本概念
运算符重载的形式:
- 运算符重载的实质是函数重载。
- 可以重载为普通函数,也可以重载为成员函数。
- 把含运算符的表达式转换为对运算符函数的调用。
- 把运算符的操作数转换为运算符函数的参数。
- 运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。
返回值类型 operator 运算符 (形参表)
{
...
}
运算符重载的实例:
#include <iostream>
using namespace std;
class Complex
{
public:
double real,imag;
Complex(double r=0.0,double i=0.0):real(r),imag(i){ }
Complex operator - (const Complex & c);
};
Complex operator +(const Complex & a,const Complex & b)
{
return Complex (a.real+b.real,a.imag+b.imag); //因为没有对象名,所以返回的是一个临时对象
}
Complex Complex::operator-(const Complex & c)
{
return Complex(real-c.real,imag-c.imag); //因为没有对象名,所以返回的是一个临时对象
}
int main()
{
Complex a(4,4),b(1,1),c;
c = a + b; //等价于 c = operator+(a,b);
cout<<c.real<<","<<c.imag<<endl;
cout<<(a-b).real<<","<<(a-b).imag<<endl;
// a-b 等价于 a.operator - (b)
system("pause");
return 0;
}
- 重载为成员函数时,参数个数为运算符操作数个数减一。
- 重载为普通函数时,参数个数为运算符操作数个数。
- c = a + b; 等价于 c = operator + (a,b);
- a - b; 等价于 a.operator - (b)
2. 赋值运算符 '=' 的重载
- 赋值运算符 '=' 只能重载为成员函数
#include <iostream>
using namespace std;
class String
{
private:
char * str;
public:
String():str(new char[1]) {str[0] = 0;}
const char * c_str() { return str; }
String & operator = (const char * s);
~String() { delete [] str;}
};
String & String::operator= (const char * s)
{
//重载“=”以使得 obj = "hello" 能够成立
delete [] str;
str = new char[strlen(s)+1];
strcpy(str, s);
return * this;
}
int main()
{
String s;
s = "Good Luck,"; //等价于 s.oprator = ("Good Luck,");
cout<<s.c_str()<<endl;
// String s2 = "hello"; //这条是初始化语句而不是赋值语句,不会调用重载的赋值运算符。编译出错
s = "Shenzhou 8!"; //等价于 s.oprator = ("Shenzhou 8!");
cout<<s.c_str()<<endl;
system("pause");
return 0;
}
当使用上面的类执行下面语句时
String s1,s2;
s1 = "this";
s2 = "that";
s1 = s2;
效果形如:(调用默认的复制构造函数,s1与s2的每个字节都相同)
造成的影响:
- "this" 所在的空间被舍弃,造成空间浪费。
- 如果 s1 对象消亡,析构函数将释放 s1.str 指向的地方。而当 s2 对象消亡时,将再一次释放。而 new 出来的只能被释放一次。
- 如果执行 s1 = "other" ; 会导致 s2.str 所指向的地方被 delete 。
因此要在 class String 里添加成员函数:
String & String::operator = (const String & s)
{
if (this == & s)
{
return * this;
}
delete [] str;
str = new char[strlen(s.str)+1];
strcpy( str, s.str);
return * this;
}
3. 可变长数组类的实现
#include <iostream>
using namespace std;
class CArray
{
int size; //数组元素的个数
int * ptr;//指向动态分配的数组
public:
CArray(int s = 0); // s 代表数组元素的个数
CArray(CArray & a);
~CArray();
void push_back(int v); //用于在数组尾部添加一个元素 v 。
CArray & operator = (const CArray & a); //用于数组对象间的赋值
int length(){ return size ;} //返回数组元素个数
int & CArray::operator [](int i) //返回值为 int 不行,
//( n = a[i] 可以,而 a[i] = 4 出错,非引用返回值不能作为赋值语句的左值)
{
//用以支持根据下标访问数组元素。如n = a[i] 和 a[i] = 4 这样的语句
return ptr[i];
}
};
CArray::CArray(int s):size(s)
{
if (s==0)
{
ptr = NULL;
}
else
{
ptr = new int[s];
}
}
CArray::CArray(CArray & a)
{
if (!a.ptr)
{
ptr = NULL;
size = 0;
return;
}
ptr = new int[a.size];
memcpy(ptr,a.ptr,sizeof(int)*a.size);
size = a.size;
}
CArray::~CArray()
{
if (ptr)
{
delete [] ptr;
}
}
CArray & CArray::operator=(const CArray & a)
{ //赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的一样
if (ptr == a.ptr) //防止 a = a 这样的赋值导致出错
{
return *this;
}
if (a.ptr == NULL) //如果 a 里面的数组是空的
{
if (ptr)
{
delete [] ptr;
}
ptr = NULL;
size = 0;
return *this;
}
if (size<a.size) //如果原有空间够大,就不用分配新的空间
{
if (ptr)
{
delete [] ptr;
}
ptr = new int[a.size];
}
memcpy(ptr,a.ptr,sizeof(int)*a.size);
size = a.size;
return *this;
}
void CArray::push_back(int v) //在数组尾部添加一个元素
{
if (ptr)
{
int * tmpptr = new int[size+1];//重新分配空间
memcpy(tmpptr,ptr,sizeof(int)*size);//拷贝原数组内容
delete [] ptr;
ptr = tmpptr;
}
else //数组本来是空的
ptr = new int[1];
ptr[size++] = v; //加入新的数组元素
}
int main()
{
CArray a; //开始里的数组是空的
for(int i=0;i<5;i++) //要用动态分配的内存来存放数组元素,需要一个指针成员变量
{
a.push_back(i);
}
CArray a2,a3;
a2 = a; //需要重载赋值运算符“=”
for(int i=0;i<a.length();++i)
{
cout<<a2[i]<<" "; // a2 是一个对象,但能像数组一样使用,需要重载“[]”运算符
}
a2 = a3; // a2 是空的
for(int i=0;i<a2.length();++i) // a2.length() 返回0
{
cout<<a2[i]<<" ";
}
cout<<endl;
a[3] = 100;
CArray a4(a); //初始化语句,需要自己写复制构造函数
for(int i=0;i<a4.length();++i)
{
cout<<a4[i]<<" ";
}
system("pause");
return 0;
}
4. 流插入运算符和流提取运算符的重载
1 流插入运算符的重载
- cout 是 iostream 中定义的 ostream 类的对象
- “ << ” 能用在 cout 上是因为在 iostream 里对 “ << ” 进行了重载。
单独实现 cout<< 5 和 cout<< "this" 的重载:
void ostream::operator <<(int n)
{
... //输出 n 的语句
return;
}
void ostream::operator <<(char * s)
{
... //输出 s 的语句
return;
}
实现 cout<< 5 << "this" 的重载 :(想要连续执行,返回值应该为 ostream 类的对象(例:cout))
ostream & ostream::operator <<(int n) //返回类型为 ostream 类的引用
{
... //输出 n 的语句
return * this; // * this 即 cout 对象,即返回 cout 对象的引用
}
ostream & ostream::operator <<(char * s)
{
... //输出 s 的语句
return * this;
}
- cout<< 5 << "this" 的函数调用形式是:cout.operator <<(5) .operator <<("this")
class CStudent
{
public:
int nAge;
};
ostream & operator <<( ostream & o, CStudent & s)
{
o<<s.nAge;
return o;
}
int main()
{
CStudent s;
s.nAge = 5;
cout<< s << "hello";
system("pause");
return 0;
}
- 上面的程序将 "<<" 重载为全局函数,参数与运算符操作数个数相同。
- 输出为 5hello 。(需要连续执行 “<<” 操作,返回值应是 ostream 类的引用)
例题:假定 c 是 Complex 类的对象,"cin>>c" 从键盘接受形式为 “a+bi” 的输入,“cout<<c” 以 “a+bi” 的形式输出 c 的值。
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class Complex
{
double real,imag;
public:
Complex(double r=0,double i=0):real(r),imag(i){ };
friend ostream & operator <<(ostream & os, const Complex & c); //因为要访问类的私有成员,声明为类的友元函数。
friend istream & operator >>(istream & is, Complex & c);
};
ostream & operator <<(ostream & os, const Complex & c)
{
os<<c.real<<"+"<<c.imag<<"i";//以 a+bi 的形式输出
return os;
}
istream & operator >>(istream & is, Complex & c)
{
string s;
is>>s; //将“a+bi”作为字符串读入
int pos = s.find("+",0);
string sTmp = s.substr(0,pos); //分离出代表实部的字符串
c.real = atof(sTmp.c_str()); //atof 库函数能将 const char* 指针指向的内容转换成 float
sTmp = s.substr(pos+1,s.length()-pos-2); //分离出代表虚部的字符串
c.imag = atof((sTmp.c_str()));
return is;
}
int main()
{
Complex c;
int n;
cin>>c>>n;
cout<<c<<","<<n;
system("pause");
return 0;
}
5. 类型转换运算符的重载
#include <iostream>
using namespace std;
class Complex
{
double real,imag;
public:
Complex (double r=0,double i=0):real(r),imag(i) { };
operator double (){return real;}
//重载强制类型转换运算符 double (返回值即为 double,所以不用指定返回值)。
};
int main()
{
Complex c(1.2,3.4);
cout<<(double) c<<endl; //输出1.2
double n = 2 + (double)c; //等价于 double n = 2 + c.operator double();
cout<<n<<endl; //输出3.2
system("pause");
return 0;
}
6. 自增、自减运算符的重载
- 前置运算符作为一元运算符重载
重载为成员函数:
T & operator ++();
T & operator --();
重载为全局函数:
T1 & operator ++(T2);
T1 & operator --(T2);
- 后置运算符作为二元运算符重载,多写一个没用的参数。(主要用于区分前置和后置)
重载为成员函数:
T & operator ++(int);
T & operator --(int);
重载为全局函数:
T1 & operator ++(T2,int);
T1 & operator --(T2,int);
- 自增、自减运算符重载的实例:
#include <iostream>
using namespace std;
class CDemo
{
private:
int n;
public:
CDemo(int i=0):n(i) { }
CDemo & operator ++(); //用于前置形式
CDemo operator ++(int); //用于后置形式 //自增运算符声明为成员函数
operator int () { return n;} //重载类型转换运算符。用于输出 cout<< CDemo对象。
friend CDemo & operator -- (CDemo &);
friend CDemo operator --(CDemo &, int); //自减运算符声明为友元全局函数
//原生态的 ++a 返回值为对象 a 的引用,(++a) = 1; //ok
// a++ 返回值为对象 a ,(a++) = 1; //error
};
CDemo & CDemo::operator++()
{//前置++
++n;
return * this;
// ++s即为:s.operator++();
}
CDemo CDemo::operator++(int k)
{//后置++
CDemo tmp(* this);//记录修改前的对象
n ++;
return tmp; //返回修改前的对象
} // s++即为:s.operator++(0)
CDemo & operator --(CDemo & d)
{
//前置 --
d.n --;
return d;
}//--s即为:operator--(s);
CDemo operator --(CDemo & d, int)
{
//后置--
CDemo tmp(d);
d.n --;
return tmp;
}//s--即为:operator--(s,0);
int main()
{
CDemo d(5);
cout<< (d++) <<","; //等价于 d.operator ++(0);
cout<< d <<",";
cout<< (++d) <<","; //等价于 d.operator ++();
cout<< d <<endl;
cout<< (d--) <<","; //等价于 operator --(d,0);
cout << d <<",";
cout << (--d) <<","; //等价于 operator --(d);
cout << d << endl;
system("pause");
return 0;
}