C++ 面向对象编程4 运算符重载
原理和机制
C++的运算符默认只支持基本类型,但是C++提供了实现类类型使用运算符的语法也就是运算符的重载。运算符的重载本质上是通过函数来实现,将类类型的运算过程写成一个特殊的函数,当对应类型遇到该运算时自动调用该函数。实现运算符的重载既可以通过成员函数,也可以使用全局函数来实现,其主要功能是进行对象与对象之间的操作,其操作逻辑也需要符合基本类型的逻辑,不然容易产生误会。
一般双目运算符的重载
1.成员函数重载(加法为例)
返回值类型 operator+(第二个对象){.....}
当在程序中碰到了 “第一个对象+第一个对象”,就会调用上面的函数,注意第一个对象的类型必须是所在运算符的重载函数的类型,第二个对象类型也必须与函数的参数类型相对应,不然不能够重载,接下来的各种运算符重载也是如此,不在一一赘述。
实验程序:分数加法运算
#include <iostream>
using namespace std;
class Fraction{
public:
explicit Fraction(int x = 0, int y = 1):x(x), y(y){}; //explicit 不允许隐式转换
void print()
{
cout<<this->x<<"/"<<this->y<<endl;
}
Fraction operator+(const Fraction &fb)
{
return Fraction(this->x*fb.y+fb.x*this->y,this->y*fb.y);
}
private:
int x;/*分子*/
int y;/*分母*/
};
int main()
{
Fraction fa(1, 2);
Fraction fb(1, 3);
Fraction fc = fa + fb;
fc.print();
return 0;
}
输出结果:5/6
2.全局函数重载
返回值类型 operator+(第一操作数,第二操作数){......}
与成员函数重载不同全局函数重载需要传递两个参数,只需要与函数的参数类型向对应,当在程序中碰到了“第一个对象+第一个对象”,就会调用上面的函数。需要注意的是,如果类外函数无法发文类内的成员,可以使用以下方法解决:
1).将成员改为公有;
2).使用友元;
3).为成员提供访问的接口。
实验程序:
#include <iostream>
using namespace std;
class Fraction{
public:
explicit Fraction(int x = 0, int y = 1):x(x), y(y){}; //explicit 不允许隐式转换
void print()
{
cout<<this->x<<"/"<<this->y<<endl;
}
friend Fraction operator+(const Fraction &fa, const Fraction &fb);
private:
int x;
int y;
};
Fraction operator+(const Fraction &fa, const Fraction &fb)
{
return Fraction(fa.x*fb.y+fb.x*fa.y/*分子*/,fa.y*fb.y/*分母*/);
}
int main()
{
Fraction fa(1, 2);
Fraction fb(1, 3);
Fraction fc = fa + fb;
fc.print();
return 0;
}
输出结果:5/6
编译器对类类型运算的处理(加法为例):
1).当编译器遇到fa+fb,先去Fraction类中寻找一个成员函数operator+(const Fraction &),如果有,就调用该函数来计算fa+fb;
2).如果没有,就去全局函数中寻找operator+(const Fraction &,const Fraction &),如果有,就调用该函数来计算fa+fb;
3).如果没有,编译器报错。
3.类类型和基本类型之间的运算
Fraction+整数 ------- 可以用成员/全局来重载
整数+Fraction ------- 只能用全局函数来重载
双目运算符的重载
1.输入运算符的重载(cin<<)
1).先去cin对象的类型istream中找一个成员函数:operator>>(a的引用类型)
2).如果找不到,就去找一个全局函数:operator>>(istream &is,a的引用类型)
3).如果再找不到就报错
//istream是C++自定义的类,内部不能修改。所以开发者只能将cin>>重载为全局函数,
//并且将该函数声明为a对应类的友元
2.输出运算符的重载(cout<<)
1).先去cout对象的类型ostream中找一个成员函数:operator<<(a的常引用类型)
2).如果找不到,就去找一个全局函数:operator<<(ostream &os,a的常引用类型)
3).如果再找不到就报错
//ostream是C++自定义的类,内部不能修改。所以开发者只能将cout<<重载为全局函数
//并且将该函数声明为a对应类的友元
3.单目运算符的重载
假设编译器对单目运算符(#)的处理
#对象 ------ 去成员函数中找一个operator#()一个函数
如果没有,就去全局函数中找operator#(对象)
也没有就报错
对象# ------ 去成员函数中找一个operator#(int)一个函数
如果没有,就去全局函数中找operator#(对象,int)
也没有就报错
实验程序
#include <iostream>
#include <cstring>
using namespace std;
class A{
public:
explicit A(int num1, int num2 , const char* str1)
{
this->num1 = num1;
this->num2 = num2;
if(str1 != NULL)
{
this->str1 = new char[strlen(str1) + 1];
strcpy(this->str1, str1);
}
}
~A()
{
if(this->str1 != NULL)
delete[] this->str1;
}
void operator=(A& a)
{
this->num1 = a.num1;
this->num2 = a.num2;
if(a.str1 != NULL)
{
if(this->str1 != NULL)
{
delete[] this->str1;
}
this->str1 = new char[strlen(a.str1) + 1];
strcpy(this->str1, a.str1);
}
}
A& operator++()
{
this->num1++;
this->num2++;
return *this;
}
A operator++(int)
{
A tmp(this->num1, this->num2, "xxx");
this->num1++;
this->num2++;
return tmp;
}
friend ostream& operator<<(ostream& _cout, const A& a);
friend istream& operator>>(istream& _cin, A& a);
private:
int num1;
int num2;
char* str1;
};
ostream& operator<<(ostream& _cout,const A& a)
{
return _cout<<a.str1<<":"<<"num1:"<<a.num1<<" num2:"<<a.num2<<endl;
}
istream& operator>>(istream& _cin, A& a)
{
return _cin>>a.num1>>a.num2;
}
int main()
{
//创建两个对象进行初始化
A a1(0, 0, "xxx");
A a2(0, 0, "xxx");
//输入运算符的重载
cin>>a1;
//赋值运算符的重载
a2 = a1;
//输出运算符的重载
cout<<a1<<a2;
//自加运算符重载
cout<<a2++;
cout<<++a1;
}
输出结果:
特殊运算符的重载
1.数组对象
当对象遇到[]运算符时,在成员函数中找一个operator[](int ...)函数,如果没有就报错。
2.强转和函数对象
1)强转重载
当编译器遇到 (类型)对象 语法时,就会去对象对应类中寻找一个 operator 类型() 的成员函数,找不到报错;
2)函数对象
当编译器遇到 对象(实参) 语法时,就会去对象对应类中寻找一个 operator()(参数) 的成员函数,找不到报错。
#include <iostream>
#include <cstring>
using namespace std;
class myarray{
public:
myarray(int size=10):size(size)
{
this->pdata = new int[size];
memset(this->pdata, 0, size*sizeof(int));
}
~myarray()
{
delete[] this->pdata;
}
//重写拷贝构造
myarray(const myarray &arr)
{
this->size = arr.size;
//申请空间
this->pdata = new int[this->size];
//将内容拷贝过来
for(int i=0;i<arr.size;i++){
this->pdata[i] = arr.pdata[i];
}
}
myarray& operator=(const myarray& arr)
{
//防止自赋值
if(this==&arr)
return *this;
//先释放旧空间
delete[] this->pdata;
this->size = arr.size;
this->pdata = new int[this->size];
for(int i=0; i<arr.size; i++)
{
this->pdata[i] = arr.pdata[i];
}
return *this;
}
//重载[]运算
int& operator[](int n)
{
if(n>=0 && (n < this->size))
return this->pdata[n];
}
//重载(强转):求首元素的值
operator int()
{
return this->pdata[0];
}
//重载():函数对象,用来求数组的和
int operator()(const myarray& arr)
{
int sum = 0;
for(int i = 0; i<arr.size; i++)
sum +=arr.pdata[i];
return sum;
}
friend ostream& operator<<(ostream& os, const myarray& arr);
private:
int *pdata;
int size;
};
ostream& operator<<(ostream& os, const myarray& arr)
{
for(int i=0; i<arr.size-1; i++)
os<<arr.pdata[i]<<",";
return os<<arr.pdata[arr.size-1];
}
int main()
{
myarray arr1(15);
myarray arr2;
arr2 = arr1;
cout<<arr1<<endl;
arr1[5] = 99;
cout<<arr1[5]<<endl;
cout<<arr1<<endl;
cout<<arr2<<endl;
arr2[0] = 100;
arr2[1] = 1;
arr2[2] = 2;
arr2[3] = 3;
arr2[4] = 4;
arr2[5] = 5;
arr2[6] = 6;
cout<<(int)arr2<<endl;
cout<<arr2<<endl;
cout<<arr1(arr2)<<endl;
return 0;
}
3.指针对象
当编译器遇到 对象-> 语法时,就会去对象对应类中寻找一个 operator->() 的成员函数,找不到报错;
当编译器遇到 对象 语法时,就会去对象对应类中寻 operator() 的成员函数,找不到报错,使用指针对象通常是为了管理其他对象的指针。
#include <iostream>
using namespace std;
class A{
public:
A(){cout<<"A()"<<endl;}
~A(){cout<<"~A()"<<endl;}
void show()
{
cout<<"show"<<endl;
}
};
class myauto_ptr{
public:
myauto_ptr(A *p=NULL):a_ptr(p)
{
cout<<"myauto_ptr()"<<endl;
}
~myauto_ptr()
{
cout<<"~myauto_ptr()"<<endl;
delete a_ptr;
}
A *operator->()
{
return this->a_ptr;//返回A类对象的地址,需要用->进行访问
}
A& operator*()
{
return *this->a_ptr; //返回A类对象本身,放回的值可以用.进行访问
}
private:
//要管理的指针
A *a_ptr;
};
int main()
{
A *a1 = new A;
myauto_ptr p1(a1);
p1->show();
(*p1).show();
}