在C++11标准中规定了四个智能指针:
std::auto_ptr
std::unique_ptr
std::shared_ptr
std::weak_ptr
它们都用来设计辅助管理动态分配对象的生命周期,即确保这些对象在正确 的时间(包括发生异常时)用正确的方式进行回收,以确保不会产生内存泄露
C++98尝试用std::auto_ptr来标准化,后来成为C++11中的std::unique_ptr的行为,为了达到目 标,move语法是不可少的,但是,C++98当时还没有move语法, 所以做了个妥协方案:利用 拷贝操作来模拟move,而如果拷贝一个std::auto_ptr会将它设置为 null,而且std::auto_ptr不能在容器中使用, 导致在实际编码中很少应用std::auto_ptr,std::unique_ptr做到了std::auto_ptr所能做到的所有事情,而且它的实现还更高效
std::unique_ptr 具现了独占语义,一个非空的 std::unique_ptr 永远拥 有它指向的对象,move一个 std::unique_ptr 会将所有权从源指针转向目的指针(源指针指向 为null)。拷贝一个 std::unique_ptr 是不允许的,假如说真的可以允许拷贝 std::unique_ptr , 那么将会有两个 std::unique_ptr 指向同一块资源区域,每一个都认为它自己拥有且可以摧毁那块资源,导致多次释放。因此,std::unique_ptr 是一个move-only类型。当它面临析构时,一个非空的std::unique_ptr 会摧毁它所拥有的资源。默认情况下, std::unique_ptr 会使用delete来释放它所包裹的原生指针指向的空间
默认情况下,析构函数会使用delete。但是,我们也可以在它的构造过程中指定特定的析构方法(custom deleters): 当资源被回收时,传入的特定的析构方法(函数对象,或者是特定的 lambda表达式)会被调用
当 std::unique_ptr 用到了自定义的deleter时,函数指针类型的deleter会使得 std::unique_ptr 的大小增长到一个字节到两个字节。对于deleters是函数对象的 std::unique_ptr ,大小的改变依赖于函数对象内部要存储多少状态。无状态的函数对象(如,没有captures的lambda expressions) 不会导致额外的大小开销。这就意味着当一 个自定义的deleter既可以实现为一个函数对象或者一个无捕获状态的lambda表达式时,所以lambda是第一优先选择。带有过多状态的函数对象的deleters是使得 std::unique_ptr 的大小得到显著的增加
关于std::unique_ptr的大小增加的测试代码:
class Person
{
public:
Person()
{
}
Person(int nAge,string strName)
{
m_nAge = nAge;
m_strName = strName;
}
virtual ~Person()
{
}
void PrintInfo()
{
cout << m_strName << endl;
cout << m_nAge << endl;
}
private:
int m_nAge;
string m_strName;
};
auto DeletePerson = [](Person* pPerson)
{
cout << "DeletePerson" << endl;
if (nullptr == pPerson)
{
return;
}
delete pPerson;
pPerson = nullptr;
};
void DeletePersonAsFunPointer(Person* pPerson)
{
cout << "DeletePersonAsFunPointer" << endl;
if (nullptr == pPerson)
{
return;
}
delete pPerson;
pPerson = nullptr;
};
template<typename...Ts>
std::unique_ptr<Person, decltype(DeletePerson)> CreatePerson(int nType,Ts&&... params)
{
std::unique_ptr<Person, decltype(DeletePerson)> pPerson(nullptr,DeletePerson);
cout << "CreatePerson sizeof of unique_ptr:" << sizeof(pPerson) << endl;
pPerson.reset(new Person(std::forward<Ts>(params)...));
return pPerson;
};
template<typename...Ts>
std::unique_ptr<Person, decltype(DeletePersonAsFunPointer)* > CreatePersonWithFuntionPointer(int nType, Ts&&... params)
{
std::unique_ptr<Person, decltype(DeletePersonAsFunPointer)*> pPerson(nullptr, DeletePersonAsFunPointer);
cout << "CreatePersonEx sizeof of unique_ptr:" << sizeof(pPerson) << endl;
pPerson.reset(new Person(std::forward<Ts>(params)...));
return pPerson;
};
{
auto tmp1 = CreatePerson(1,10, "zhangsan");
auto tmp2 = CreatePersonWithFuntionPointer(2,10, "zhangsan");
}
输出:
CreatePerson sizeof of unique_ptr:4
CreatePersonEx sizeof of unique_ptr:8
DeletePersonAsFunPointer
DeletePerson
std::unique_ptr 会产生两种格式,一种是独立的对象(std::unique_ptr),另外一种是数组(std::unique_ptr<T[]> )。 因此,std::unique_ptr指向的内容从来不会产生任何歧义性。它的API是专门为了你使用的格式来设计的。例如,单对象格式中没有过索引操作符(操作符[]),数组格式则没有解引用操作符(操作符”*“和操作符”->”)
C++11使用 std::unique_ptr 来表述独占所有权。但是它的一项最引人注目的特性就是它可以轻易且有效的转化为 std::shared_ptr
std::unique_ptr 是一个具有开销小,速度快, move-only 特定的智能指针,使用独占拥有方式来管理资源
默认情况下,释放资源由delete来完成,也可以指定自定义的析构函数来替代。但是具有丰富状态的deleters和以函数指针作为deleters增大了 std::unique_ptr的存储开销
很容易将一个std::unique_ptr转化为std::shared_ptr