自增运算符重载

之前我们了解了如何实现对两个复数对象实现相加操作,而我们熟知的运算符比如+=,++i,i++等操作暂时还没能正常使用

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class Complex {
public:
Complex(const double& dreal, const double& dimage)
:_dreal(dreal)
, _dimage(dimage)
{
cout << "Complex(double,double)" << endl;
}
void print()
{
cout << "F(x) = " << _dreal << " + " << _dimage << 'i' << endl;
}
Complex(const Complex& c)
:_dreal(c._dreal)
, _dimage(c._dimage)
{
;
}
~Complex()
{
cout << "~Complex" << endl;
}
friend Complex operator + (const Complex& lhs, const Complex& rhs);
private:
double _dreal;
double _dimage;
};

Complex operator + (const Complex& lhs, const Complex& rhs)
{
Complex temp(lhs._dreal + rhs._dreal, lhs._dimage + rhs._dimage);
return temp;
}
void test0()
{
Complex c1(1, 2);
Complex c2(3, 4);

Complex c3 = c1 + c2;

c3 += c1;//error
++c3;//error
c3++;//error
}
int main()
{
test0();
return 0;
}

重构+=操作

class Complex{
public:
Complex& operator += (const Complex& rhs)
{
this->_dreal += rhs._dreal;
this->_dimage += rhs._dimage;

return *this;
}
private:
double _dreal;
double _dimage;
};

重构++i操作

class Complex{
public:
Complex operator ++()
{
_dreal++;
_dimage++;

return *this;
}
private:
double _dreal;
double _dimage;
};

重构i++操作

class Complex{
public:
Complex operator ++(int)//int仅仅是用来标识重构的是i++操作,int没有任何实际意义
{
Complex temp(*this);

++_dreal;
++_dimage;
return temp;//还原i++操作,i++为先反应后自增
}
private:
double _dreal;
double _dimage;
};

上述操作中有一些函数定义中+=和++i带有引用符号,而i++则没有带,原因是i++返回的是临时变量,为右值无法进行取地址操作,而前两个返回的是this指针

由此也看出++i操作比i++操作要简便不少

总实现代码

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class Complex {
public:
Complex(const double& dreal, const double& dimage)
:_dreal(dreal)
, _dimage(dimage)
{
cout << "Complex(double,double)" << endl;
}
void print() const
{
cout << "F(x) = " << _dreal << " + " << _dimage << 'i' << endl;
}
Complex(const Complex& c)
:_dreal(c._dreal)
, _dimage(c._dimage)
{
cout << "Complex(const Complex&)" << endl;
}
~Complex()
{
cout << "~Complex" << endl;
}
friend Complex operator + (const Complex& lhs, const Complex& rhs);

Complex& operator += (const Complex& rhs)
{
this->_dreal += rhs._dreal;
this->_dimage += rhs._dimage;

return *this;
}

Complex& operator ++()
{
_dreal++;
_dimage++;

return *this;
}

Complex operator ++(int)//int仅仅是用来标识重构的是i++操作,int没有任何实际意义
{
Complex temp(*this);

++_dreal;
++_dimage;
return temp;//还原i++操作,i++为先反应后自增
}
private:
double _dreal;
double _dimage;
};

Complex operator + (const Complex& lhs, const Complex& rhs)
{
Complex temp(lhs._dreal + rhs._dreal, lhs._dimage + rhs._dimage);
return temp;
}
void test0()
{
Complex c1(1, 2);
Complex c2(3, 4);

c1.print();
c2.print();

Complex c3 = c1 + c2;
c3.print();

c3 += c1;
c3.print();

(++c3).print();
(c3++).print();
c3.print();
}
int main()
{
test0();
return 0;
}
Complex(double,double)
Complex(double,double)
F(x) = 1 + 2i
F(x) = 3 + 4i
Complex(double,double)
F(x) = 4 + 6i
F(x) = 5 + 8i
F(x) = 6 + 9i
Complex(const Complex&)
F(x) = 6 + 9i
~Complex
F(x) = 7 + 10i
~Complex
~Complex
~Complex

输入输出重载运算符

重构输入输出的操作就在于他们的输入输出运算符>>,<<

输出运算符重载

首先我们先对print成员函数优化一下,使他复数的表现形式更加标准规范

#include <iostream>

using std::endl;
using std::cout;

class Complex {
public:
Complex(const double& dreal, const double& dimage)
:_dreal(dreal)
, _dimage(dimage)
{
cout << "Complex(double,double)" << endl;
}
~Complex()
{
cout << "~Complex" << endl;
}
void print() const
{
cout << "f(x) = ";
if (!_dreal && !_dimage) cout << 0;

if (_dreal)
{
cout << _dreal;
if (_dimage > 0) cout << "+";
}
if (_dimage)
{
if (_dimage == 1) cout << "i";
else if (_dimage == -1) cout << "-i";
else cout << _dimage << 'i';
}
cout << endl;

}
private:
double _dreal;
double _dimage;
};

void test1()
{
Complex c1(1, 2)
, c2(1, 0)
, c3(1, -2)
, c4(-1, 2)
, c5(-1, 0)
, c6(-1, -2)
, c7(0, 2)
, c8(0, 0)
, c9(0, -2);

c1.print();
c2.print();
c3.print();
c4.print();
c5.print();
c6.print();
c7.print();
c8.print();
c9.print();
}
int main()
{
test1();
return 0;
}

可以发现在我们输出一连串对象的时候会频繁调用print函数(因为调用需要一个.所以用起来非常难受)

<< 也是一种运算符,我们可以利用重构<<的方法省去调用函数的操作,观察可得,重构<<运算符需要两个参数,类似于cout的输出函数和其右边相应的值
那么我们就可以得到重构<<的运算符重载声明
ostream& operator <<(ostream& os,const Complex& rhs)

class Complex{
public:
//在类中定义时的写法
//std::ostream& operator <<(std::ostream& os)
//该写法将右边的参数当做隐藏参数放在了第一个参数的位置上,违反了操作数的左右位置,于是我们更希望使用friend友元的方式完整的写出参数列表
friend std::ostream& operator <<(std::ostream& os,const Complex& rhs);
private:
double _dreal;
double _dimage;
};
std::ostream& operator <<(std::ostream& os,const Complex& rhs)
{
//先把输出的值放在os流中(单独一个cout)
os << "f(x) = ";
if (!rhs._dreal && !rhs._dimage) os << 0;

if (rhs._dreal)
{
os << rhs._dreal;
if (rhs._dimage > 0) os << "+";
}
if (rhs._dimage)
{
if (rhs._dimage == 1) os << "i";
else if (rhs._dimage == -1) os << "-i";
else os << rhs._dimage << 'i';
}
os << endl;

//将os流中的数据全数输出
return os;
}

优化后代码

#include <iostream>

using std::endl;
using std::cout;

class Complex {
public:
Complex(const double& dreal, const double& dimage)
:_dreal(dreal)
, _dimage(dimage)
{
cout << "Complex(double,double)" << endl;
}
~Complex()
{
cout << "~Complex" << endl;
}
friend std::ostream& operator <<(std::ostream& os, const Complex& rhs);
private:
double _dreal;
double _dimage;
};

std::ostream& operator <<(std::ostream& os, const Complex& rhs)
{
//先把输出的值放在os流中(单独一个cout)
os << "f(x) = ";
if (!rhs._dreal && !rhs._dimage) os << 0;

if (rhs._dreal)
{
os << rhs._dreal;
if (rhs._dimage > 0) os << "+";
}
if (rhs._dimage)
{
if (rhs._dimage == 1) os << "i";
else if (rhs._dimage == -1) os << "-i";
else os << rhs._dimage << 'i';
}
os << endl;

//将os流中的数据全数输出
return os;
}

void test1()
{
Complex c1(1, 2)
, c2(1, 0)
, c3(1, -2)
, c4(-1, 2)
, c5(-1, 0)
, c6(-1, -2)
, c7(0, 2)
, c8(0, 0)
, c9(0, -2);

cout << c1 << c2 << c3 << c4 << c5 <<c6 << c7 << c8 << c9 << endl;
}
int main()
{
test1();
return 0;
}
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
f(x) = 1+2i
f(x) = 1
f(x) = 1-2i
f(x) = -1+2i
f(x) = -1
f(x) = -1-2i
f(x) = 2i
f(x) = 0
f(x) = -2i

~Complex
~Complex
~Complex
~Complex
~Complex
~Complex
~Complex
~Complex
~Complex

输入运算符重载

实际上就是对对象内的私有成员输入,重构格式与输出类似

class Complex{
public:
friend std::istream& operator >>(std::istream& is,Complex& rhs);//不能带const因为对象的值需要修改
private:
double _dreal;
double _dimage;
};

void readDouble(std::istream& is,double& number)
{
while(is>>number,!is.eof())
{
if(is.bad())
{
cout<<"istream has broken"<<endl;
return;
}
if(is.fail())
{
is.clear();
//第一个参数为流的最大输入字节数
is.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
cout<<"please input a interger number"<<endl;
}
else
{
cout<<"number:"<<number<<endl;
return;
}
}
}
std::istream& operator >>(std::istream& is.Complex& rhs)
{
readDouble(rhs._dreal);
readDouble(rhs._dimage);
}

代码

#include <iostream>

using std::endl;
using std::cout;

class Complex {
public:
Complex()
:_dreal(0)
,_dimage(0)
{
cout<<"Complex()"<<endl;
}
Complex(const double& dreal, const double& dimage)
:_dreal(dreal)
, _dimage(dimage)
{
cout << "Complex(double,double)" << endl;
}
~Complex()
{
cout << "~Complex" << endl;
}
friend std::istream& operator >>(std::istream& is, Complex& rhs);
private:
double _dreal;
double _dimage;
};

void readDouble(std::istream& is, double& number)
{
while (is >> number, !is.eof())
{
if (is.bad())
{
cout << "istream has broken" << endl;
return;
}
if (is.fail())
{
is.clear();
//第一个参数为流的最大输入字节数
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
cout << "please input a interger number" << endl;
}
else
{
cout << "number:" << number << endl;
return;
}
}
}
std::istream& operator >>(std::istream& is,Complex& rhs)
{
readDouble(is,rhs._dreal);
readDouble(is,rhs._dimage);

return is;
}

void test1()
{
Complex c1;

std::cin>>c1;

}
int main()
{
test1();
return 0;
}

函数调用重载

函数调用重载是一种比较特殊的函数重载,之前的函数重载是对同一名称的函数以不同参数为根据重载,函数调用重载在此基础上,通过对象来进行调用

#include <iostream>

using std::cout;
using std::endl;

class Func {
public:
Func()
:count(0)
{
;
}
int operator()(int x, int y)
{
++count;
return x + y;
}
int operator()(int x, int y, int z)
{
++count;
return x + y + z;
}
private:
int count;
};

int add(int x, int y)
{
return x + y;
}
int add(int x, int y, int z)
{
return x + y + z;
}
void test0()
{
Func fun;

//Func两种调用方式
cout << fun(1, 2) << endl;
cout << fun(1, 2, 3) << endl;
cout << fun.operator()(1, 2) << endl;
cout << fun.operator()(1, 2, 3) << endl;

//普通函数调用以及重载
cout << add(1, 2) << endl;
cout << add(1, 2, 3) << endl;

//指针调用
typedef int (*aptr)(int, int);
aptr addptr = add;
cout << addptr(1, 2) << endl;
}
int main()
{
test0();
}

下标访问运算符重载

在我们采用string类创建对象时,我们发现string对象可以直接用下标访问对应的字符,并且可以进行修改,接下来我们可以尝试模仿string类的这一特点来重载下表访问运算符

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class CharArray{
public:
CharArray(size_t& sz)
:_data(new char[sz]())
,_sz(sz)
{
;
}
int sz()
{
return _sz;
}
void release()
{
delete [] _data;
_data = nullptr;
}
~CharArray()
{
if(_data) release();
cout<<"~Complex"<<endl;
}
private:
size_t _sz;
char* _data;
};
void test0()
{
const char* pstr = "Hello World";

CharArray(strlen(pstr) + 1);

for(int i = 0;i < ca.sz();++i)
{
ca[i] = pstr[i];
}
}
int main()
{
test0();
}

此时报错显示 没有与这些操作数匹配的 “[]” 运算符

所以我们还需要重构[]运算符

class CharArray{
public:
char& operator [](size_t idx)
{
cout << "char& operator []" << endl;
if (idx > _sz)//因为类型为size_t,不会有负数的情况
{
cout << "Beyond the Array!" << endl;

static char charnull = '\0';
return charnull;//因为返回的临时对象,加上static修饰使其在全局区,让charnull生命周期比函数更大
}
else
{
return _data[idx];
}
}
private:
size_t _sz;
char* _data;
};

此时我们还想模仿string,使得我们的CharArray对象能够通过cout直接输出

class CharArray{
public:
friend std::ostream& operator <<(std::ostream& os,const CharArray& ca);
private:
char* _data;
size_t _sz;
};

std::ostream& operator <<(std::ostream& os,const CharArray& ca)
{
for(size_t i = 0;i < ca.sz();++i)
{
os<<ca[i];
}
return os;
}

代码

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class CharArray {
public:
CharArray(const size_t& sz)
:_data(new char[sz]())
, _sz(sz)
{
;
}
size_t sz() const
{
return _sz;
}
void release()
{
delete[] _data;
_data = nullptr;
}
~CharArray()
{
if (_data) release();
cout << "~Complex" << endl;
}

char& operator [] (size_t& idx)
{
cout << "char& operator []" << endl;
if (idx > _sz)//因为类型为size_t,不会有负数的情况
{
cout << "Beyond the Array!" << endl;

static char charnull = '\0';
return charnull;
//因为返回的临时对象,加上static修饰使其在全局区,让charnull生命周期比函数更大
}
else
{
return _data[idx];
}
}
friend std::ostream& operator <<(std::ostream& os, CharArray& ca);
private:
size_t _sz;
char* _data;
};

std::ostream& operator <<(std::ostream& os, CharArray& ca)
{
for (size_t i = 0; i < ca.sz(); ++i)
{
os << ca[i] <<' ';
}
return os;
}
void test0()
{
const char* pstr = "Hello World";

CharArray ca(strlen(pstr) + 1);

for (size_t i = 0; i < ca.sz(); ++i)
{
ca[i] = pstr[i];
}

cout << ca << endl;
}
int main()
{
test0();
}

<<重载时对象不能带const的原因

在函数过程中,如果发生了数组越界,那么我们之前所构建的下标重构中会返回charnull,虽然charnull是全局区,但因为引用符号实际上对ca的某个值发生了赋值操作,修改了对象,const不允许这样的操作出现,因此带上const发生了报错