一、运算符重载的概念
首先,我们提出两个问题,第一:为什么会有运算符重载?第二:什么是运算符重载?
第一,所谓运算符,就是“+ - * / "等此类运算符,只能用于对基本类型的常量、变量进行运算,不能用于对象之间的运算。
而我们为了对象之间也可以用,两个对象可以进行+/-等其他运算操作,便产生了“运算符重载”。
第二,什么是“运算符重载”呢?
以运算符和特定的关键字作为某个函数的函数名,传递给函数的实参作为运算符的操作数,函数的返回值作为运算的结果。
二、运算符重载详解
运算符重载函数既可以是成员函数,也可以是非成员函数。
【注:C++规定,运算符“=” 只能重载为成员函数】
1.二元运算符
下面以重载(+)为例:
#include <iostream>
#include <cstring>
using namespace std;
class Int
{
public:
int value;
Int(int v = 0) : value(v)//有默认参构造函数
{
cout << "Create Int: " << value << " " << this << endl;
}
~Int() //析构函数
{
cout << "Destory Int: " << value << " " << this << endl;
}
Int(const Int& c) //拷贝构造函数
{
value = c.value;
cout << "Copy Int:" << this << endl;
}
//运算符重载
//对象 + 对象 :a+b;
Int operator + (const Int& c) const
{
return Int(this->value + c.value);
}
//对象 + 常量 :a+10;
Int operator + (const int x) const
{
return Int(this->value + x);
}
};
//常量 + 对象 :10+a;
Int operator +(const int x, const Int& c)
{
return Int(x + c.value);
}
int main()
{
Int a(10), b(15);
Int c;
c = a + b;
cout << "a+b= " << c.value << endl; //25
cout << "a+10= " << (a + 10).value << endl; //20
cout << "5+a= " << (10+a).value << endl; //15
}
运行结果:
由结果可以看到:调用了6次构造函数(结果中十六进制数为对象的地址)。
举例分析一下a+b的运行过程:
1.构造了对象a,并初始化为10;
2.构造了对象b,并初始化为15;
3.构造了对象c,未初始化,默认为0;
4.执行”c=a+b",调用了重载“+”成员函数(对象+对象),构造了一个临时的不具名对象去返回函数值,其值为25;
5.a+b的值已返回,析构临时对象;
6.打印。
重载"+"成员函数:成员函数参数比运算符目数少1。
a+b ->a.operator+(b)->operator(&a,b)
重载"+"非成员函数:非成员函数参数等于运算符目数。
5+a-> operator+(5,&a)
2.关系运算符
下面以重载(>)为例:
#include <iostream>
#include <cstring>
using namespace std;
class Int
{
public:
int value;
Int(int v = 0) : value(v)//有默认参构造函数
{
cout << "Create Int: " << value << " " << this << endl;
}
~Int() //析构函数
{
cout << "Destory Int: " << value << " " << this << endl;
}
// 重载 >
bool operator > (const Int& c)const
{
return this->value > c.value;
}
};
int main()
{
Int a=1,b=2;
cout << " a = " << a.value << endl;
cout << " b = " << b.value << endl;
if ( a > b )
cout << " a > b" << endl;
else
cout << " a < b" << endl;
return 0;
}
运行结果:
3.赋值运算符=
#include <iostream>
#include <cstring>
using namespace std;
class Int
{
public:
int value;
Int(int v = 0) : value(v)//有默认参构造函数
{
cout << "Create Int: " << value << " " << this << endl;
}
~Int() //析构函数
{
cout << "Destory Int: " << value << " " << this << endl;
}
// 重载 =
Int& operator=(const Int& c)
{
if (this != &c) { //防止自身赋值
value = c.value;
}
cout << "operator="<<endl;
return *this;
}
};
int main()
{
Int a=1,b;
cout << " a = " << a.value << endl;
b = a;
cout << " b = " << b.value << endl;
return 0;
}
运行结果:
//a=b->a.operator=(b)->operator=(&a,b)
4.类型强转
适用于类的对象值进行比较。(具体实例见重载++代码)
operator int() const
{
return value;
}
5.重载++/--
下面以重载(++)为例:
class Int
{
public:
int value;
Int(int v = 0) : value(v)//有默认参构造函数
{
cout << "Create Int: " << value << " " << this << endl;
}
~Int() //析构函数
{
cout << "Destory Int: " << value << " " << this << endl;
}
Int(const Int& c) //拷贝构造函数
{
value = c.value;
cout << "Copy Int:" << this << endl;
}
void Print() const //打印
{
cout << value << endl;
}
//前置++
Int& operator++() {
this->value += 1;
return *this;
}
//后置++
Int operator++(int)
{
Int old = *this;
++* this; //从右向左结合
return old;
}
//强转
operator int() const
{
return value;
}
};
int main()
{
cout << "前置++:"<<endl;
for (Int i = 0; i < 3; ++i)
{
i.Print();
}
cout << endl<< "后置++:" << endl;
for (Int i = 0; i < 3; i++)
{
i.Print();
}
return 0;
}
运行结果:
6.重载()
示例:
#include<iostream>
#include <cstring>
using namespace std;
class Add
{
private:
int sum;
public:
Add() :sum(0){}
//在类型里面重载(),称为仿函数
int operator()(const int a, const int b) //二元仿函数
{
sum = a + b;
return sum;
}
};
int main()
{
int a = 10, b = 20, c = 0;
c = Add()(a, b);
cout << "c = "<< c << endl;
}
运行结果:
c = 30
分析:
c = Add()(a, b)-->c=Add().operator()(a,b)
首先,由Add()构造函数构建了一个不具名对象;其次由该不具名对象调用()重载。
三、前置++和后置++辨析
1.前置++和后置++的运算规则
(1)a++; (2)++a;
分析:a++是先进行取值,后进行自增。++a是先进行自增,后进行取值。
2.返回引用和返回对象
首先,我们再来看一下前置++和后置++的实现代码。
//前置++
Int& operator++() {
this->value += 1;
return *this;
}//后置++
Int operator++(int)
{
Int old = *this;
++* this; //从右向左结合
return old;
}
两者的区别是:前置++返回引用(地址),后置++返回对象。
为什么会有上述区别?这是为了保持前置++和后置++的运算符特性。
1.前置++取成员变量自增后的值。故应分为以下步骤:
- 成员变量先进行自增;
- 返回对象的引用(地址)。
2. 后置++取成员变量自增前的值。故应分为以下步骤:
- 先构建一个临时对象保存成员变量自增前的值;
- 成员变量进行自增;
- 返回临时对象。
前置++:a=++b->a=b.operator++()->a=operator(&b);
后置++:a=b++->a=b.operator++(0)->a=operator(&b,0);
3.为什么前置++返回引用,而后置++返回对象?
前置++的成员变量的值会被修改并需要返回,不能返回普通对象。若返回引用,则直接修改并用this指针接收。【引用本质上是指针】
后置++返回对象为什么没有问题呢?因为后置++只需要自增并不返回。自增后值直接用this指针接收(++* this),并没有用临时对象接收并返回,临时对象返回的是自增前的值。
3.效率分析
下面对前置++和后置++的效率分析。
由运行结果可以看出,前置++的效率更高。
前置++:运行时只进行了一次对象的创建和析构,即对象i。
后置++:运行过程中不断地进行对象的创建和析构。
- 先创建了对象i,打印 i=0;
- 调用重载后置++,拷贝构造old对象和返回的临时对象(将亡值对象),析构old对象和临时对象,打印 i=1;以此类推...
- 最后析构对象i。
【如有错误,欢迎指正,谢谢大家!】