一个简易的unique_ptr实现
unique_ptr关键在于几个构造函数与移动函数。unique_ptr拥有两部分数据,一部分是裸指针,另一部分是删除器,本份代码实现不包括删除器,所以它的大小与裸指针相同。
unique_ptr主要的创建方式有两种,一是通过裸指针创建,二是通过一个右值unique_ptr对象进行移动构造。裸指针创建不需要解释,但是无论是标准unique_ptr版本还是这份实现,都是允许使用指针作为一个左值传入unique_ptr的构造函数中。为了实现仅能通过右值对象移动构造,需要将拷贝构造与拷贝赋值函数都声明为delete:
unique_ptr(const unique_ptr&)=delete;
unique_ptr& operator=(const unique_ptr&)=delete;
实现移动构造(移动赋值同理)时,需要使用一个函数模板,来使得不同类型的指针之间可以实现转换(如子类指针转化为基类指针)。标准库的实现利用了enable_if模板,它通过辅助的模板判断两个指针之间是否可以安全的转化,并利用判断结果产生合适的代码。这里就粗糙一点,直接用模板上了:
template<typename Ep>
unique_ptr(unique_ptr<Ep>&& rhs):raw_p(rhs.release()){}
这份实现的接口和标准unique_ptr相同(除了没有删除器),故release()
函数就是当前unique_ptr释放对于资源的控制权,并返回相应的裸指针;reset(pointer p=nullptr)
接收一个可选参数,它会重新设置当前unique_ptr所管理的资源。
智能指针能够表现的像一般指针,C++强大的操作符重载能力不可或缺。首先便是两个解引用符号:
reference operator*() const
{
return *raw_p;
}
pointer operator->() const
{
return &(this->operator*());
}
这两个解引用符号第一个比较好理解,第二种形式有些奇怪,能记住就行。基本上这类成对出现的操作符,都是用其中一个实现另外一个。
bool
运算符以及!
运算符可以用于指针的条件判断,像if(!up)
之类都是程序员常用的判断指针是否为空的方式。bool类型转换运算符很简单,因为这两个操作一般也是一起出现的,所以也是通过bool运算符来实现!运算符的重载:
operator bool() const
{
return raw_p!=nullptr;
}
bool operator!() const
{
return !(this->operator bool());
}
全部代码如下:
#include<string>
#include<vector>
#include<iostream>
template<typename Up>
class unique_ptr
{
using pointer = Up*;
using reference = Up&;
public:
unique_ptr():raw_p(nullptr){}
unique_ptr(pointer rhs):raw_p(rhs){}
template<typename Ep>
unique_ptr(unique_ptr<Ep>&& rhs):raw_p(rhs.release()){}
template<typename Ep>
unique_ptr& operator=(unique_ptr<Ep>&& rhs)
{
reset(rhs.release());
return *this;
}
unique_ptr(const unique_ptr&)=delete;
unique_ptr& operator=(const unique_ptr&)=delete;
~unique_ptr() noexcept
{
if(raw_p)
delete raw_p;
}
reference operator*() const
{
return *raw_p;
}
pointer operator->() const
{
return &(this->operator*());
}
operator bool() const
{
return raw_p;
}
bool operator!() const
{
return !(this->operator bool());
}
bool operator==(const unique_ptr& rhs) const
{
return raw_p==rhs.raw_p;
}
bool operator!=(const unique_ptr& rhs) const
{
return !(this->operator==(rhs));
}
pointer release()
{
pointer temp=raw_p;
raw_p=nullptr;
return temp;
}
void reset(pointer p=nullptr)
{
pointer temp=raw_p;
raw_p=p;
if(temp!=nullptr)
delete temp;
}
void swap(const unique_ptr& rhs) noexcept
{
std::swap(raw_p,rhs.raw_p);
}
private:
pointer raw_p;
};
template<typename T,typename...Args>
unique_ptr<T> make_unique(Args&&...args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
int main()
{
unique_ptr<std::string> up1(new std::string("up1"));
std::string *sp=new std::string("up2");
unique_ptr<std::string> up2(sp);
auto up3(make_unique<std::string>("up3"));
std::cout<<*up1<<" "<<*up2<<" "<<*up3<<" \n";
}