在面向对象的语言中,如果想对对象进行加、减、赋值等操作,用普通的运算符就会报错,当然可以定义相应用来实现这些功能的函数,但是在类中定义很麻烦,因此提出了运算符重载的概念。在C++中,使用关键词operator来定义,定义方式如下:
返回值 operator运算符 () {实现;}
注意:C++不允许重载内置运算符的含义,包括指针类型,因此返回值如果是int就会报错!
1、前置自加运算符重载
参见下面的程序:用于实现前置运算符的自加,把对象成员数值加1,并返回一个指向当前对象的指针,用于将返回的对象赋给一个新的对象。
#include <iostream>
using namespace std;
class A
{
public:
A(){n=1;cout<<"构造函数执行"<<endl;}
A(const A&s){n=s.n;cout<<"复制构造函数执行"<<endl;}
~A(){cout<<"析构函数执行"<<endl;}
int get() const {return n;}
void set(int x){n=x;}
void add(){n++;}
A operator++(){++n;return *this;}
private:
int n;
};
int main()
{
A a;
cout<<"初始化对象a的成员值:"<<a.get()<<endl;
a.add();
cout<<"执行add()对象a的成员值:"<<a.get()<<endl;
A b=++a;//自加运算符重载后,执行调用重载后的函数,即第10行定义的函数
cout<<"执行前置自加运算后对象a的成员值:"<<a.get()<<endl;
cout<<"新建对象b的成员值:"<<b.get()<<endl;
return 0;
}
运行结果:
分析:
1)main函数第一行,创建对象a,执行构造函数并初始化成员n的值为1
2)在类中定义了一个add函数,用于对成员变量进行操作,执行后的作用是使对象a的数据成员n加1
3)执行A b=++a时,调用运算符重载函数,并返回指向当前对象的指针*this,传递给新的对象,对象传递过程中需调用复制构造函数,而重载运算符的函数作用也是让对象的值自加,由输出结果可见。
4)由于整个程序创建了两个对象,因此调用两次析构函数析构对象。
讨论:
如果把第22行改为a=++a,则不会调用复制构造函数,因为没有创建新对象!
2、重载后置自加运算符
后置自加与前置自加的区别是后置自加会先返回值,再加1,而前置自加是先加1再返回。因此在重载后置自加运算符时,先创建一个临时对象,把当前对象的值赋给这个临时对象,再把当前对象的数据自加,之后按值返回临时对象。注意必须按值返回,否则临时对象超出作用域会被析构掉!
我们和上一个程序写在一起,为了避免重复,在这个后置自加重载函数后加一个无用的参数,程序参考如下:
#include <iostream>
using namespace std;
class A
{
public:
A(){n=1;cout<<"构造函数执行"<<endl;}
A(const A&s){n=s.n;cout<<"复制构造函数执行"<<endl;}
~A(){cout<<"析构函数执行"<<endl;}
int get() const {return n;}
void set(int x){n=x;}
void add(){n++;}
A operator++(){++n;return *this;}
A operator++(int o){A temp(*this);++n;return temp;}
private:
int n;
};
int main()
{
A a;
cout<<"初始化对象a的成员值:"<<a.get()<<endl;
a.add();
cout<<"执行add()对象a的成员值:"<<a.get()<<endl;
A b=++a;//自加运算符重载后,执行调用重载后的函数,即第10行定义的函数
cout<<"执行前置自加运算后对象a的成员值:"<<a.get()<<endl;
cout<<"新建对象b的成员值:"<<b.get()<<endl;
A c=a++;//自加运算符重载后,执行调用重载后的函数,即第10行定义的函数
cout<<"执行后置自加运算后对象a的成员值:"<<a.get()<<endl;
cout<<"新建对象c的成员值:"<<c.get()<<endl;
return 0;
}
输出结果:
分析:
第26行调用重载后的后置自加运算符时,先构造一个临时对象,把n=3时的对象a赋给这个临时对象,之后把a中的n加1,再把临时对象返回,并把这个返回的对象副本赋给新对象c,由于这个临时对象中n的值并没有自加,因此c的成员值输出为3;
3、重载加法运算符
重载方法:把“+”后的对象作为参数传递进来与源对象的值相加,并利用构造函数返回一个对象赋给一个新的对象。程序如下:
#include <iostream>
using namespace std;
class A
{
public:
A(){}
A(int i){n=i;}
int get() const {return n;}
void set(int x){n=x;}
const A operator+(const A&c){return A(n+c.get());}
private:
int n;
};
int main()
{
A a(1),b(2),c;
c=a+b;
cout<<"a:"<<a.get()<<endl;
cout<<"b:"<<b.get()<<endl;
cout<<"c:"<<c.get()<<endl;
return 0;
}
输出结果:
4、重载赋值运算符
赋值运算符与加法运算符重载方式类似,将“=”后的对象传递给=函数中,把数据成员的值赋给临时对象,之后返回一个指向当前对象的指针赋给新的对象。
#include <iostream>
using namespace std;
class A
{
public:
A(){}
A(int i){n=i;}
int get() const {return n;}
void set(int x){n=x;}
A operator=(A&c){n=c.get();return *this;}
private:
int n;
};
int main()
{
A a(1),c;
c=a;
cout<<"a:"<<a.get()<<endl;
cout<<"c:"<<c.get()<<endl;
return 0;
}
输出结果:
讨论:
这样重载方式属于浅层复制,按值传递是没问题的,如果类A的成员变量改为指针,就会出现两个指针指向同一块内存区域的问题,删除一个会导致另一个称为迷途指针,解决方法是实用深层复制的方法,即分别为传递进来的对像指针开辟一块内存区域,也把要赋给的对象的指针保存在另一区域中,如下面的程序:
#include <iostream>
using namespace std;
class A
{
public:
A(){n=new int;cout<<"不带参数的构造函数"<<endl;}
A(int i){n=new int;*n=i;cout<<"带一个参数的构造函数"<<endl;}
~A(){delete n;n=0;cout<<"析构函数调用"<<endl;}
int get() const {return *n;}
void set(int x){*n=x;}
const A &equal(const A&c){*n=c.get();return *this;}
const A &operator=(const A&c){*n=c.get();return *this;}
private:
int *n;
};
int main()
{
A *a=new A(1);
A b;
b=*a;
cout<<"a:"<<a->get()<<endl;
cout<<"b:"<<b.get()<<endl;
delete a;
cout<<"b:"<<b.get()<<endl;
return 0;
}
输出结果: