C++内部定义的数据类型(int , float, …)的数据操作可以用运算符号来表示,其使用形式是表达式;用户自定义的类型的数据的操作则用函数表示,其使用形式是函数调用。为了使对用户自定义数据类型的数据的操作与内置的数据类型的数据的操作形式一致,C++提供了运算符的重载,通过把C++中预定义的运算符重载为类的成员函数或者友元函数,使得对用户的自定义数据类型的数据—对象的操作形式与C++内部定义的类型的数据一致。
运算符重载规则:
返回类型 operator 运算符(形参列表)
1、可以重载的运算符有:
双目运算符 + - * / %
关系运算符 == != < > <= >=
逻辑运算符 || && +
单目运算符 + - * &
自增自减运算符 ++ --
位运算符 | & ~ ^ << >>
赋值运算符 = += -= *= /= %= &= |= ^= <<= >>=
空间申请和释放 new delete new[] delete[]
其他运算符 ( ) -> ->* , []
|
2、不可以重载的运算符有:
成员访问符 .
成员指针访问运算符 *
域运算符 ::
长度运算符 sizeof
条件运算符号 ?:
|
3、重载操作符必须具有一个类类型或者是枚举类型的操作数
int operator+(int, int); //不能重载
4、优先级和结合性是固定的:
——操作符的优先级、结合性或操作数个数不能改变
X == Y + Z;
5、不再具备短路求值特性:
——重载操作符并不保证操作数的求值顺序 && || &
6、不能臆造并重载一个不存在的运算符:
——如@, #,$等;
运算符重载的方式分为下面3种:
1. 采用普通函数的重载形式
2. 采用成员函数的重载形式
3. 采用友元函数的重载形式
|
以普通函数形式重载运算符: (+,-,*, /)
普通函数形式重载运算符,要求待操作的属性必须是public类型的
#include <iostream>
using namespace std;
class Complex
{
public:
double _real;
double _imag;
public:
Complex(double real=0,double imag=0):_real(real),_imag(imag)
{
cout<<"Complex(double,double)"<<endl;
}
void print()
{
cout<<_real;
if(_imag>0)
cout<<"+"<<_imag<<"i"<<endl;
else if(_imag<0)
cout<<"-"<<_imag*(-1)<<"i"<<endl;
else
cout<<endl;
}
};
|
//操作的属性必须是public
Complex operator+ (const Complex & lhs,const Complex & rhs)
{
return Complex( lhs._real + rhs._real, lhs._imag + rhs._imag );
}
int main(
{
Complex a1(1,-2);
Complex a2(0,-4);
a1.print();
a2.print();
Complex a3=a1+a2;
a3.print();
return 0;
}
|
以成员函数形式重载运算符:(+=,-=,++,--)
1、成员函数形式的运算符声明和实现与成员函数类似,首先应当在类定义中声明该运算符,声明的具体形式为:
返回类型 operator 运算符(参数列表);
2、既可以在类定义的同时定义运算符函数使其成为inline型,也可以在类定义之外定义运算符函数,但要使用作用域限定符“::”,类外定义的基本格式为:
返回类型 类名::operator 运算符(参数列表){……}
3、用成员函数重载双目运算符时,左操作数无须用参数输入,而是通过隐含的this指针传入,这种做法的效率比较高
#include <iostream>
using namespace std;
class Complex
{
private:
double _real;
double _imag;
public:
Complex(double real=0,double imag=0):_real(real),_imag(imag)
{
cout<<"Complex(double,double)"<<endl;
}
void print()
{
cout<<_real;
if(_imag>0)
cout<<"+"<<_imag<<"i"<<endl;
else if(_imag<0)
cout<<"-"<<_imag*(-1)<<"i"<<endl;
else
cout<<endl;
}
//左参数this指针传入(a1)
Complex operator+ (const Complex & rhs);
};
Complex Complex::operator+ (const Complex & rhs)
{
return Complex(_real+rhs._real,_imag+rhs._imag); //调用构造函数,默认拷贝构造函数
}
int main()
{
Complex a1(1,-2);
Complex a2(0,-4);
a1.print();
a2.print();
Complex a3=a1+a2; // 这里也会调用默认拷贝构造函数
a3.print();
return 0;
}
|
#include <iostream>
using namespace std;
class Complex
{
private:
double _real;
double _imag;
public:
Complex(double real=0,double imag=0):_real(real),_imag(imag)
{
cout<<"Complex(double,double)"<<endl;
}
void print()
{
cout<<_real;
if(_imag>0)
cout<<"+"<<_imag<<"i"<<endl;
else if(_imag<0)
cout<<"-"<<_imag*(-1)<<"i"<<endl;
else
cout<<endl;
}
Complex& operator+ (const Complex & rhs);
};
Complex& Complex::operator+ (const Complex & rhs)
{
this->_real+=rhs._real; //this可加可不加,不加也是默认有this指针
this->_imag+=rhs._imag;
return *this; //*this就是a1,这里就不会调用拷贝构造函数
}
int main()
{
Complex a1(1,-2);
Complex a2(0,-4);
a1.print();
a2.print();
Complex a3=a1+a2; //调用默认拷贝构造函数
a3.print();
return 0;
}
|
#include <iostream>
using namespace std;
class Complex
{
private:
double _real;
double _imag;
public:
Complex(double real=0,double imag=0):_real(real),_imag(imag)
{
cout<<"Complex(double,double)"<<endl;
}
void print()
{
cout<<_real;
if(_imag>0)
cout<<"+"<<_imag<<"i"<<endl;
else if(_imag<0)
cout<<"-"<<_imag*(-1)<<"i"<<endl;
else
cout<<endl;
}
Complex & operator+= (const Complex & rhs)
{
_real+=rhs._real;
_imag+=rhs._imag;
return *this;
}
};
|
Complex operator+ (const Complex &lhs,const Complex & rhs)
{
Complex tmp(lhs);
tmp+=rhs;
return tmp;
}
int main()
{
Complex a1(1,-2);
Complex a2(0,-4);
a1.print();
a2.print();
Complex a3=a1+a2;
a3.print();
return 0;
}
|
#include <iostream>
using namespace std;
class Complex
{
private:
double _real;
double _imag;
public:
Complex(double real=0,double imag=0):_real(real),_imag(imag)
{
cout<<"Complex(double,double)"<<endl;
}
void print()
{
cout<<_real;
if(_imag>0)
cout<<"+"<<_imag<<"i"<<endl;
else if(_imag<0)
cout<<"-"<<_imag*(-1)<<"i"<<endl;
else
cout<<endl;
}
Complex& operator++ (); //前置++
Complex operator++ (int); //后置++
};
Complex& Complex::operator++ ()
{
++(this->_real);
++(this->_imag);
return *this;
}
Complex Complex::operator++(int)
{
Complex tmp(*this);
++(this->_real);
++(this->_imag);
return tmp;
}
|
int main()
{
Complex a1(1,-2);
Complex a2(0,-4);
a1.print();
a2.print();
cout<<"++a1.print()"<<endl;
(++a1).print();
cout<<"a1.print()"<<endl;
a1.print();
cout<<"a2++.print()"<<endl;
(a2++).print();
cout<<"a2.print()"<<endl;
a2.print();
return 0;
}
|
以友元函数形式重载运算符:
1、操作符还可重载为友元函数形式,这将没有隐含的参数this指针。对双目运算符,友元函数有2个参数,对单目运算符,友元函数有一个参数。
friend 返回类型operator 运算符 (参数表);
#include <iostream>
using namespace std;
class Complex
{
private:
double _real;
double _imag;
public:
Complex(double real=0,double imag=0):_real(real),_imag(imag)
{
cout<<"Complex(double,double)"<<endl;
}
void print()
{
cout<<_real;
if(_imag>0)
cout<<"+"<<_imag<<"i"<<endl;
else if(_imag<0)
cout<<"-"<<_imag*(-1)<<"i"<<endl;
else
cout<<endl;
}
friend Complex operator+ (const Complex & lhs,const Complex & rhs);
};
|
Complex operator+ (const Complex &lhs,const Complex & rhs)
{
return Complex(lhs._real+rhs._real,lhs._imag+rhs._imag);
}
int main()
{
Complex a1(1,-2);
Complex a2(0,-4);
a1.print();
a2.print();
Complex a3=a1+a2;
a3.print();
return 0;
}
|
友员函数形式和成员函数形式的比较
1、对于绝大多数可重载操作符来说,两种重载形式都是允许的。但对下标运算符[] 、赋值运算符=、函数调用运算符()、指针运算符->,只能使用成员函数形式。
对于如下代码:
complex c1(1.0, 2.0), cRes;
cRes = c1 + 5; //#1
cRes = 5 + c1; //#2
友元函数形式重载的都是合法的,可转换成:
cRes = operator+(c1, 5); //#1 合法
cRes = operator+(5, c1); //#2 合法
但成员函数形式的重载,只有语句#1合法,语句#2非法
cRes = c1.operator+(complex(5)); //#1 可能合法
cRes = 5.operator+(c1); //#2 非法,5不会隐式转换成complex
|
对运算符重载的补充说明
运算符重载可以改变运算符内置的语义,如以友元函数形式定义的加操作符
complex operator + (const complex& C1,const complex& C2)
{
return complex( C1.real - C2.real, C1.imag - C2.imag);
}
|
注**明明是加操作符,但函数内却进行的是减法运算,这是合乎语法规则的,不过却有悖于人们的直觉思维,会引起不必要的混乱,因此,除非有特别的理由,尽量使重载的运算符与其内置的、广为接受的语义保持一致。
赋值运算符=:
1、赋值运算是一种很常见的运算,如果不重载赋值运算符,编译器会自动为每个类生成一个缺省的赋值运算符重载函数,先看下面的语句:
对象1=对象2;
2、实际上是完成了由对象2各个成员到对象1相应成员的复制,其中包括指针成员,这和复制构造函数和缺省复制构造函数有些类似,如果对象1中含指针成员,并且牵扯到类内指针成员动态申请内存时,问题就会出现。
●注意下述两个代码的不同:
类名 对象1=对象2; //复制构造函数
●和
类名 对象1; //默认构造函数
对象1=对象2; //赋值运算符函数
|
函数调用运算符():
1、函数调用运算符()同样只能重载为成员函数形式。其形式为:
返回类型 operator()(arg1,arg2,……) // 参数个数可以有多个,没有限制。
2、针对如下定义:
void computer :: operator () () {} ;
int computer :: operator () (int x) {};
char computer :: operator () (char x, char y) {};
|
computer com1;
int z = com1(3200);
char c = com1(‘a’, ‘b’);
|
4、一个类如果重载了函数调用operator(),就可以将该类对象作为一个函数使用,这样的类对象也称为函数对象。函数也是一种对象,这是泛型思考问题的方式。
#include <iostream>
using namespace std;
class Demo
{
public:
//函数调用运算符能传递多个参数,能够重载
double operator()(double x, double y)
{
return x > y ? x : y;
}
double operator()(double x, double y, double z)
{
return (x + y) * z;
}
};
|
int main(void)
{
Demo demo; //函数对象
cout << "3, 4中较大的数是" << demo(3, 4) << endl;
cout << "(3 + 4) * 5 = " << demo(3, 4, 5) << endl;
//cout << "(3 + 4) * 5 = " << demo.operator()(3, 4, 5) << endl;
return 0;
}
|
下标运算符[]:
1、下标运算符是个二元运算符,C++编译器将表达式 sz[x]; 解释为 sz.operator[](x); 一般情况下,下标运算符的重载函数原型如下:
返回类型& operator[](参数类型);
2、下标运算符的重载函数只能有一个参数,不过该参数并没有类型限制,任何类型都可以,如果类中未重载下标运算符,编译器将会给出下标运算符的缺省定义,此时,参数必须是int型,并且要声明数组名才能使用下标变量,如computer com[3]; 则com[1]等价于com.operator[](1),如果[]中的参数类型非int型,或者非对象数组要使用下标运算符时,需要重载下标运算符[]。
#include <iostream>
#include<string.h>
using namespace std;
static char null='\0'; //放外面也可以
class CharArr
{
private:
int _size;
char * _pbuf;
public:
CharArr(int size):_size(size)
{
_pbuf = new char[_size];
}
~CharArr()
{
delete _pbuf;
}
int length()
{
return _size;
}
char& operator [](int idx)
{
//static char null='\0';
//多次进入函数,看到有静态变量已经存在,就不会再定义,写在这里说明作用域只在这个函数
if(idx>=0&&idx<_size)
{
return _pbuf[idx];
}
else
{
cout<<"下标越界"<<endl;
return null;
}
}
};
|
int main()
{
const char *p="hello world!";
CharArr ca(strlen(p)+1);
for(int idx=0;idx!=ca.length();++idx)
{
ca[idx]=p[idx];
}
for(int idx=0;idx!=ca.length();++idx)
{
cout<<ca[idx];
}
cout<<endl;
return 0;
}
|
new和delete重载:
通过重载new和delete,我们可以自己实现内存的管理策略。new和delete只能重载为类的静态运算符。而且重载时,无论是否显示指定static关键字,编译器都认为是静态的运算符重载函数。
重载new时,必须返回一个void *类型的指针,它可以带多个参数,但第1个参数必须是size_t类型,该参数的值由系统确定。
static void* operator new(size_t nSize)
{
cout << "new操作符被调用, size = " << nSize << endl;
void* pRet = new char[nSize];
return pRet;
}
|
static void operator delete(void* pVoid)
{
cout << "delete操作符被调用." << endl;
delete []pVoid;
}
|
重载delete时必须返回void类型,它可以带有多个参数,第1个参数必须是要释放的内存的地址void *,如果有第2个参数,它必须为size_t类型。
#include<iostream>
#include<string.h>
using namespace std;
class CSstudent
{
public:
static void* operator new(size_t nsize) // nsize的值由操作系统自己确定
{
cout<<"operator new() size = "<<nsize<<endl;
void *tmp = new char[nsize]; // char的单位刚好是1
//operator new运算符有两个版本,new(size_t)和new[](size_t)
return tmp;
}
static void operator delete(void* tmp)
{
cout<<"operator delete()"<<endl;
delete []tmp;
}
public:
int _id; // size = 4;
char _name[3]; // size = 3;
};
|
//内存对齐 4 + 3 +1 = 8;
int main()
{
CSstudent* s1 = new CSstudent;
s1->_id = 110;
strcpy(s1->_name,"hello,world"); //这里就是大小不够也能匹配
cout<<sizeof(*s1)<<endl;
int* a = new int(5); //不会调用类内部的new运算符
delete s1;
}
|
输入>>输出<<的重载:
//不能流对象复制 (ifstream ifs2(ifs));,不然程序存在两个流对象,导致读写不一致
1、>>和<<运算符只能重载为友元函数形式。
2、返回值必须是流对象引用
#include <iostream>
using namespace std;
//如果一个对象能够进行复制,表达的是值语义
//如果一个对象不能进行复制,表达的是对象语义:
class A
{
private:
//禁止复制:从语法层面去表达语义
A(const A &); //拷贝构造函数
A& operator=(const A &); //赋值构造函数
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl; //链式编程
}
};
|
int main(void)
{
A a; //栈对象的创建需要析构函数
//A b = a; //Error,A类创建的对象不能进行复制
A c;
//c = a;
//Error,A类创建的对象不能调用拷贝构造函数复制
return 0;
}
所以,重载输入输出流必须返回对象的引用
|
#include <iostream>
#include<stdio.h>
using namespace std;
class Complex
{
private:
double _real;
double _imag;
public:
Complex(int real=0,int imag=0):_real(real),_imag(imag){}
friend ostream & operator << (ostream & os,const Complex & rhs);
friend istream & operator >> (istream & is, Complex & rhs);
friend Complex operator + (const Complex & lhs,const Complex & rhs);
};
ostream & operator << (ostream & os,const Complex & rhs)
{
os<<rhs._real;
if(rhs._imag>0)
{
os<<"+"<<rhs._imag<<"i";
}
else
os<<"-"<<rhs._imag*(-1)<<"i";
return os;
}
istream & operator >> (istream & is, Complex & rhs)
{
is>>rhs._real;
while(is.get()!='*')
is>>rhs._imag;
return is;
}
Complex operator + (const Complex & lhs,const Complex & rhs)
{
return Complex(lhs._real+rhs._real,lhs._imag+rhs._imag);
}
|
int main()
{
Complex c1(1,-2);
Complex c2(0,-4);
cout<<"c1 = "<<c1<<endl;
cout<<"c2 = "<<c2<<endl;
Complex c3=c1+c2;
cout<<"c3 = "<<c3<<endl;
Complex c4;
cin>>c4;
cout<<"c4 = "<<c4<<endl;
return 0;
}
|
指针运算符->的重载:
//当类中只有一个指针类型的数据成员才能重载 -> ,有多个就不行
#include <iostream>
using namespace std;
class Data
{
public:
int size()
{
return 5;
}
};
class DataPtr
{
private:
Data *_pData;
public:
DataPtr():_pData ( new Data )
{
cout<<"DataPtr()"<<endl;
}
~DataPtr()
{
delete _pData;
}
Data * operator -> ( )
{
return _pData;
}
};
|
int main()
{
DataPtr p;
cout<< p -> size() <<endl;
cout<< (p.operator -> ())->size()<<endl;
return 0;
}
|