unique_ptr使用
1.介绍
unique_ptr是独占型智能指针,不允许其他指针共享内部指针,不允许赋值
unique_ptr<T> myptr(new T);
unique_ptr<T> myOther = myptr; // 错误不能复制
unique_ptr 可以通过函数返回给其他的unique_ptr,还可以通过std::move来转移unique_ptr,这样它本身不再拥有原来指针的所有权。
unique_ptr<T> myptr(new T);
unique_ptr<T> myOther = std::move(myptr); // ok
unique_ptr<T> ptr = myptr; // 错误不能转移,不能复制
unique_ptr 没有make_unique函数在C++11中,在C++14中支持,如下实现make_unique方法:
//make_unique的实现
//支持普通指针
template<class T, class... Args> inline
typename enable_if<!is_array<T>::value, unique_ptr<T> >::type make_unique(Args&&... args)
{return unique_ptr<T>(new T(std::forward<Args>(args)...));
}//支持动态数组
template<class T> inline
typename enable_if<is_array<T>::value && extent<T>::value==0, unique_ptr<T> >::type make_unique(size_t size)
{typedef typename remove_extent<T>::type U;return unique_ptr<T>(new U[size]());
}//过滤掉定长数组的情况
template<class T, class... Args>
typename enable_if<extent<T>::value != 0, void>::type make_unique(Args&&...) = delete;
如果不是数组,直接创建unique_ptr。如果是数组,先派单是否是定长数组,如果是定长数组编译不通过,如果是非定长数组,则获取数组中的元素类型,根据size创建动态数组。
==unique_ptr和shared_ptr相比除了独占特性,还可以指向一个数组。==如下代码:
std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9; // 设置最后一个元素值为9
std::shared_ptr<int []> ptr(new int[10]); // 代码不合法
2.指定删除器
与shared_ptr相比,unique_ptr指定删除器的时候需要确定删除器类型,所以不能像shared_ptr那样直接指定删除器。
std::shared_ptr<int> ptr(new int(1),[](int* p){ delete p;}); // 正确
std::unique_ptr<int> ptr1(new int(1),[](int* p){ delete p;}); // 错误
std::unique_ptr<int,void(*)(int*)> ptr(new int(1),[](int* p){ delete p;}); // lamda在没有捕获变量时正确,如果捕获了变量就会编译报错
上述代码原因是在没有捕获变量时可以直接转化为函数指针,一旦捕获了就无法转换成函数指针了。
如果希望unique_ptr的删除器支持lamda,可以写成:
std::unique_ptr<int, std::function<void(int*)>> ptr(new int(1),[&](int* p){delete p;});
也可以自定义删除器
struct MyDelete
{void operator(int* p){delete p;}
};
int main()
{std::unique_ptr<int,MyDelete> p(new int(1));return 0;
}
3.使用场景
如果只希望一个智能指针管理资源或者管理数组使用unique_ptr,如果希望多个指针管理一个资源使用shared_ptr。
4.effective 介绍
class Investment { // 投资
public:virtual ~Investment() {}
};
class Stock : public Investment {}; // 股票
class Bond : public Investment {}; // 债券
class RealEstate : public Investment {}; // 不动产void makeLogEntry(Investment*) {}auto delInvmt1 = [](Investment* pInvestment) { // custom deleter(a lambda expression), 使用无状态lambda表达式作为自定义析构器makeLogEntry(pInvestment);delete pInvestment;};void delInvmt2(Investment* pInvestment) // 使用函数作为自定义析构器
{makeLogEntry(pInvestment);delete pInvestment;
}template<typename... Ts>
//std::unique_ptr<Investment> makeInvestment(Ts&&... params) // return std::unique_ptr to an object created from the given args
std::unique_ptr<Investment, decltype(delInvmt1)> makeInvestment(Ts&&... params) // 改进的返回类型,返回值尺寸与Investment*相同
//std::unique_ptr<Investment, void(*)(Investment*)> makeInvestment(Ts&&... params) // 返回值尺寸等于Investment*的尺寸+至少函数指针的尺寸
{//std::unique_ptr<Investment> pInv(nullptr);std::unique_ptr<Investment, decltype(delInvmt1)> pInv(nullptr, delInvmt1); // ptr to be returnedif (nullptr/* a Stoc object should be created*/) {pInv.reset(new Stock(std::forward<Ts>(params)...));} else if (nullptr/*a Bond object should be created*/) {pInv.reset(new Bond(std::forward<Ts>(params)...));} else if (nullptr/*a RealEstate object should be created*/) {pInv.reset(new RealEstate(std::forward<Ts>(params)...));}return pInv;
}int test_item_18()
{auto pInvestment = makeInvestment(/*arguments*/); // pInvestment is of type std::unique_ptr<Investment>std::shared_ptr<Investment> sp = makeInvestment(/*arguments*/); // converts std::unique_ptr to std::shared_ptrreturn 0;
} // destroy *pInvestment
C++11中共有四种智能指针:std::auto_ptr、std::unique_ptr、std::shared_ptr和std::weak_ptr。所有这些智能指针都是为管理动态分配对象的生命期而设计的,换言之,通过保证这样的对象在适当的时机以适当的方式析构(包括发生异常的场合),来防止资源泄漏。
std::auto_ptr是个从C++98中残留下来的弃用特性,它是一种对智能指针进行标准化的尝试,这种尝试后来成为了C++11中的std::unique_ptr。
std::unique_ptr可以做std::auto_ptr能够做的任何事,并且不止于此。它执行的效率和std::auto_ptr一样高,而且不用扭曲其要表达的本意去复制任何对象。它从任何方面来看都要比std::auto_ptr更好。
每当你需要使用智能指针时,std::unique_ptr基本上应是手头首选。可以认为在默认情况下(使用默认析构器)std::unique_ptr和裸指针(raw pointer)有着相同的尺寸,并且对于大多数的操作(包括提领(including dereferenceing)),它们都是精确地执行了相同的指令。
std::unique_ptr实现的是专属所有权(exclusive ownership)语义。一个非空的std::unique_ptr总是拥有其所指涉到的资源。移动一个std::unique_ptr会将所有权从源指针移至目标指针(源指针被置空)。std::unique_ptr不允许复制(copy),因为如果复制了一个std::unique_ptr,就会得到两个指涉到同一资源的std::unique_ptr,而这两者都认为自己拥有(因此应当析构)该资源。因而std::unique_ptr是个只移型别(move-only type)。在执行析构操作时,由非空的std::unique_ptr析构其资源。默认地,资源的析构是通过对std::unique_ptr内部的裸指针实施delete完成的。
std::unique_ptr的一个常见用法是在对象继承谱系中作为工厂函数的返回型别。
默认地,析构通过delete运算符实现,但是在析构过程中std::unique_ptr可以被设置为使用自定义析构器(custom delete):析构资源所调用的任意函数(或函数对象,包括那些由lambda表达式产生的)。
std::unique_ptr以两种形式提供,一种是单个对象(std::unique_ptr),另一种是数组(std::unique_ptr<T[]>)。单个对象形式不提供索引运算符(operator[]),而数组形式则不提供提领运算符(lack dereferencing operator)(operator*和operator->)。
std::unique_ptr是C++11中表达专属所有权的方式,但它还有一个十分吸引人的特性,就是std::unique_ptr可以方便高效地转换成std::shared_ptr。
要点速记:
(1).std::unique_ptr是小巧、高速的、具备只移型别的智能指针,对托管资源实施专属所有权语义。
(2).默认地,资源析构采用delete运算符来实现,但可以指定自定义删除器。有状态的删除器和采用函数指针实现的删除器会增加std::unique_ptr型别的对象尺寸。
(3).将std::unique_ptr转换成std::shared_ptr是容易实现的。