一.函数调用运算符
我们知道,普通函数执行时,有一个特点就是无记忆性,一个普通函数执行完毕,它所在的函数栈空间就会被销毁,所以普通函数执行时的状态信息,是无法保存下来的,这就让它无法应用在那些需要对每次的执行状态信息进行维护的场景。而有了对象的存在,对象执行某些操作之后,只要对象没有销毁,其状态就是可以保留下来的。
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
class Complex
{
private:
int _real;
int _image;
int _count; //记录函数对象的状态
public:
Complex(int real=0,int image=0,int count =0);
~Complex();
//ostream 没有拷贝构造函数
friend std::ostream &operator<<(std::ostream& os ,const Complex& rhs);
int operator()(int a,int b);
};
Complex::Complex(int real,int image,int count)
:_real(real)
,_image(image)
,_count(count)
{
}
Complex::~Complex()
{
}
std::ostream &operator<<(std::ostream &os ,const Complex& rhs)
{
if(rhs._real!=0)
{
if(rhs._image>0)
os<<rhs._real<<" + "<<rhs._image<<"i";
else if(rhs._image<0)
os << rhs._real << " - " << (-1) * rhs._image << "i";
else
os<<rhs._real;
}
else
{
if(rhs._image>0)
os<<rhs._image<<"i";
else if(rhs._image<0)
os<<rhs._image<<"i";
else
os<<rhs._real;
}
return os;
}
int Complex::operator()(int a,int b)
{
return a+b;
++count;
}
int main()
{
Complex c;
cout<<c(1,4)<<endl;
cout<<c.operator()(3,6)<<endl;
}
从例子可以看出,一个类如果重载了函数调用operator(),就可以将该类对象作为一个函数使用。对于这种重载了函数调用运算符的类创建的对象,我们称为函数对象,函数也是一种对象。
二.下标访问运算符重载
优势:通过数组下标运算符我们可以不用再考虑数组越界问题。
#include <iostream>
#include <math.h>
#include <string.h>
using std::cin;
using std::cout;
using std::endl;
class Complex
{
private:
int _size;
char *_string;
public:
Complex(int real);
~Complex();
char &operator[](size_t num);
int size();
friend void test();
};
Complex::Complex(int real)
: _size(real), _string(new char[_size]())
{
}
Complex::~Complex()
{
if (_string != nullptr)
{
delete[] _string;
_string = nullptr;
}
}
char &Complex::operator[](size_t num)
{
if (num < _size)
{
return _string[num];
}
else
{
static char over='\0';
return over;
}
}
int Complex::size()
{
return _size;
}
void test()
{
const char* str="hello,world!";
Complex a(strlen(str)+1);
for (int i = 0; i < a.size(); i++)
{
a[i]=str[i];
}
for (size_t i = 0; i < a.size(); i++)
{
cout << a.operator[](i) << " ";
}
cout << endl;
}
int main()
{
test();
}
通过下表访问运算符解决了数组越界的问题,有一点需要注意就是下表访问运算符的重载返回值问题
char &Complex::operator[](size_t num)
{
if (num < _size)
{
return _string[num];
}
else
{
static char over='\0';
return over;
}
}
因为我们要改变私有变量_string[]数组中的内容,所以这时我们要加上引用使他称为一个左值,如果返回的是一个右值是不可以改变_sting中的内容的。其次因为我们返回的是引用所以要考虑返回变量的生命周期问题,所以要将over变量设置成静态成员变量。
我们总结一下什么时候要返回值要加引用:
1.如果返回类型是类类型时,可以加上引用符合减少调用拷贝构造函数。
2.如果要一个左值时,如上需要加引用。
三.成员访问运算符
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
class Data
{
private:
int _data;
public:
int get_Data() const
{
return _data;
}
};
class MiddleLayer
{
private:
Data* _pdata;
public:
MiddleLayer(Data* data)
:_pdata(data)
{
cout<<"MiddleLayer()"<<endl;
}
~MiddleLayer()
{
cout<<"~MiddleLayer()"<<endl;
if(_pdata!=nullptr)
{
delete _pdata;
_pdata=nullptr;
}
}
Data* operator->()
{
return _pdata;
}
Data& operator*()
{
return *_pdata;
}
};
class ThirdLayer
{
private:
MiddleLayer* _ml;
public:
ThirdLayer(MiddleLayer* ml)
:_ml(ml)
{
cout<<"ThirdLayer()"<<endl;
}
~ThirdLayer()
{
cout<<"~ThirdLayer()"<<endl;
if(_ml != nullptr)
{
delete _ml;
_ml=nullptr;
}
}
MiddleLayer* operator->()
{
return _ml;
}
MiddleLayer& operator*()
{
return *_ml;
}
};
void test()
{
MiddleLayer m(new Data());
cout<<m->get_Data()<<endl<<m.operator->()->get_Data()<<endl<<endl;
cout<<"-----------------"<<endl;
cout<<(*m).get_Data()<<endl<<m.operator*().get_Data()<<endl;
ThirdLayer tl(new MiddleLayer(new Data()));
cout<<tl.operator->()->operator->()->get_Data()
<<" = "<<tl->operator->()->get_Data()<<endl;
cout<<tl.operator*().operator*().get_Data()
<<" = "<<tl.operator*().operator->()->get_Data()
<<" = "<<(*tl)->get_Data()<<endl;
}
int main()
{
test();
return 0;
}
总结:箭头->访问运算符只能以成员函数进行重载,其返回值必须是一个指针或者是重载运算符的对象。MiddleLayer m(new Data());创建栈对象,用m对象来管理new Data()堆空间,会自动调用析构函数对堆空间进行回收,不用考虑内存泄漏问题。