一、概念、
在自定义行为类似指针的类时,需要重载*和->。C++中的智能指针就重载了这两个运算符。->必须是成员函数,*也应该是成员函数。与内置类型保持一致,这两个函数通常都是const的。以为*和->通常不会也不应该改变对象的状态
*运算符的返回值通常都是一个类型的引用(因为类内部的数据不一定非得指向T的对象,也可能指向一个T的派生类对象。如果是这样并且operator*返回的是个T对象,而非一个引用(代表真正的派生类对象),那么,operator*函数便是返回一个错误类型的对象!,也就是导致子类对象转基类对象的切割问题)
->运算符的返回值必须是一个类的指针。因为当自定义类型的operator->被调用时,编译器会将operator->转化为
(obejct.operator->())->member
所以,operator->的返回值必须是一个指针
二、示例
通过实现一个智能指针来熟悉这两个运算符的重载,类的整体实现如下
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
class mysmartpointer
{
public:
mysmartpointer(T *ptr=nullptr):
ptr_(ptr),
pcount_(new size_t(0))
{
cout<<__func__<<endl;
if (ptr_==nullptr) {//空指针不增加引用计数
*(this->pcount_)=0;
}
else {
*(this->pcount_)=1;
}
}
mysmartpointer(const mysmartpointer &rval):
ptr_(rval.ptr_),
pcount_(rval.pcount_)
{
if (ptr_) {//拷贝初始化,如果指针不为空,增加引用计数
++*pcount_;
}
cout<<"mysmartpointer(const mysmartpointer &rval)"<<endl;
}
mysmartpointer &operator=(const mysmartpointer &rval)
{
cout<<__func__<<endl;
mysmartpointer t(rval);
this->swap(t);
--*pcount_;//减少左值的引用计数
if (*pcount_==0) {//此时如果引用计数为0,那么释放指向的对象和引用计数
cout<<"delete object when operator="<<endl;
delete ptr_;
delete pcount_;
}
++*rval.pcount_;//增加右值的引用计数
return *this;
}
T &operator*() const
{
cout<<__func__<<endl;
return *ptr_;//返回指向对象本身
}
T *operator->() const
{
cout<<__func__<<endl;
return &this->operator*();//通过调用operator*来实现operator->
}
explicit operator bool()
{
cout<<__func__<<endl;
return !(ptr_==nullptr && *pcount_==0);
}
void swap(mysmartpointer &t)//交换指针和引用计数
{
cout<<__func__<<endl;
std::swap(this->ptr_, t.ptr_);
std::swap(this->pcount_, t.pcount_);
}
size_t use_count()
{
cout<<__func__<<endl;
return *pcount_;
}
~mysmartpointer()
{
cout<<__func__<<endl;
if (--*pcount_==0) {//析构时,--引用计数
cout<<"delete object"<<endl;
delete ptr_;
delete pcount_;
}
}
private:
T *ptr_;
size_t *pcount_;
};
上述代码中,operator->的实现使用过operator*来实现。这两个函数一般只实现一个,另一个直接调用即可
该类中引用计数是采用指针的方式实现的,当对智能指针进行赋值、拷贝时,可以利用指针的特性让两个对象的引用计数同时指向一块内存,共享引用计数
operator=的实现也是采用swap方式,避免重新分配内存,而且可以处理自赋值的情况
测试
int main(int argc, char const *argv[])
{
mysmartpointer<string> spstr1(new string("1234"));
mysmartpointer<string> spstr2=spstr1;
cout<<spstr1.use_count()<<endl;
cout<<spstr2.use_count()<<endl;
cout<<"------"<<endl;
mysmartpointer<string> spstr3;
spstr3=spstr1;
spstr1=spstr1;
cout<<spstr3.use_count()<<endl;
cout<<spstr1.use_count()<<endl;
cout<<spstr2.use_count()<<endl;
cout<<spstr1->size()<<endl;
if (spstr1) {
cout<<"spstr1 is not nullptr"<<endl;
}
return 0;
}
参考
《C++ Primer》
《More Effective C++》
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出