前一篇文章介绍了共享指针shared_ptr,这篇介绍另一种智能指针:unique_ptr。
1、创建
与shared_ptr不同,C++11并没有提供类似std::make_shared的标准库函数来返回一个unique_ptr,但是C++14提供了类似的库函数:std::make_unique,语法如下:
std::make_unique<类型>(参数列表)
依然以Person类为例:
class Person
{
public:
Person(const std::string& strName, int iAge) :m_strName(strName), m_iAge(iAge){}
~Person(){ std::cout << "Person析构, name=" << m_strName << std::endl; }
void PrintInfo(){ std::cout << "姓名:" << m_strName << ", 年龄:" << m_iAge << std::endl; }
private:
std::string m_strName;
int m_iAge;
};
主函数如下:
int _tmain(int argc, _TCHAR* argv[])
{
{
std::unique_ptr<Person> p1 = std::make_unique<Person>("ye", 30);
p1->PrintInfo();
}
system("pause");
return 0;
}
执行结果:
很简单!一旦p1的作用域消失,便会自动释放内存。
2、所有权转移及内容清空
从unique的字面就能理解,这种类型的指针,不能与其他指针一起共享内存对象,只能唯一。如果真的需要,只能将所有权转移,转移后原来的指针无效。
转移所有权,有两种方式:
(1)p.release()
p为unique_ptr指针,放弃对指针的控制权,返回指针,并将p置为空。
看代码:
int _tmain(int argc, _TCHAR* argv[])
{
{
Person* p = nullptr;
{
std::unique_ptr<Person> p1 = std::make_unique<Person>("ye", 30);
p = p1.release();
if (p1)
std::cout << "p1非空" << std::endl;
else
std::cout << "p1已空" << std::endl;
}
p->PrintInfo();
}
system("pause");
return 0;
}
执行结果如下:
这里,在局部范围内定了一个普通的指针p,接着创建一个unique_ptr指针p1,紧接着用p1.release(注意这里是".",不是"->")将p1的控制权转移给p。可以看到,当p1的作用域结束后,并没有自动释放对象的内存。这是因为,在经过release后,p1原来的内存对象已经不再和智能指针绑定,也就无法通过智能指针自动释放。
当p的作用域结束后,也没有自动释放对象。这是因为p是普通指针,虽然它的对象是从智能指针来的,但是想要释放,还是需要手动delete,如果把p改成智能指针就可以做到自动释放。
但是需要注意一点,release返回是普通指针,如果要用智能指针接收,则需要转换一次。
代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
{
std::unique_ptr<Person> p;
{
std::unique_ptr<Person> p1 = std::make_unique<Person>("ye", 30);
p = std::unique_ptr<Person>(p1.release()); //这里不能直接p = p1.release();
}
p->PrintInfo();
}
system("pause");
return 0;
}
执行结果如下:
这里,用unique_ptr的指针p来接收p1所指向的对象,当p的作用域消失,则会自动释放内存,调用析构函数,打印相关信息。
(2)p.reset()/p.reset(k)
p为unique_ptr指针,若不传参数,则直接将p置空;若提供了普通指针k,则将p指向k的内存。
看代码:
int _tmain(int argc, _TCHAR* argv[])
{
{
std::unique_ptr<Person> p = std::make_unique<Person>("zhao", 40);
{
std::unique_ptr<Person> k = std::make_unique<Person>("ye", 30);
p.reset(k.release()); //这里p原本指向的对象"zhao"不再有智能指针指向它,所以会自动释放
if (k)
std::cout << "k不为空" << std::endl;
else
std::cout << "k为空" << std::endl;
}
p->PrintInfo();
p.reset();
if (p)
std::cout << "p不为空" << std::endl;
else
std::cout << "p为空" << std::endl;
}
system("pause");
return 0;
}
执行结果如下:
程序中,先创建了一个对象为"zhao"的unique_ptr指针p,紧接着创建一个对象为"ye"的unique_ptr指针k,再执行语句p.reset(k.release())。此时,k的对象所有权会交给p,而p原来指向的对象因为再没有任何智能指针指向它,所以会自动释放,于是打印了"zhao"的析构信息。k调用了release,已经为空,用if判断可以验证。紧接着打印p的信息,为之前k的对象"ye";再执行p.reset(),未传任何参数,所以p直接被置空,用if判断可以验证。
3、局部unique_ptr,可以作为返回值返回
这个特征和unique_ptr不能赋值有一些冲突,但是仔细想想也很合理。
局部的unique_ptr在返回时,代表其即将消失,作为返回值返回后,指针依然只有一份,完全符合unique的特性。代码如下:
std::unique_ptr<Person> GetPerson(const std::string& strName, int iAge)
{
std::unique_ptr<Person> p = std::make_unique<Person>(strName, iAge);
return p;
}
int _tmain(int argc, _TCHAR* argv[])
{
{
std::unique_ptr<Person> p = GetPerson("ye", 30);
p->PrintInfo();
}
system("pause");
return 0;
}
执行结果:
4、不能直接作为参数,需要以引用/const引用的方式作为参数
unique_ptr不能直接作为参数,需要用引用。
函数定义时没问题,但是一旦调用就会编译报错。
看代码:
void PrintInfo(std::unique_ptr<Person> pPerson) //这里直接用的是unique_ptr
{
pPerson->PrintInfo();
}
int _tmain(int argc, _TCHAR* argv[])
{
{
std::unique_ptr<Person> p = std::make_unique<Person>("ye", 30);
PrintInfo(p); //如果将这一句注释掉,编译会成功
}
system("pause");
return 0;
}
编译结果如下(vs2013):
修改PrintInfo函数,如下:
void PrintInfo(std::unique_ptr<Person>& pPerson) //参数改成了引用
{
pPerson->PrintInfo();
}
主函数不变,运行结果如下:
至此,C++11提出来的两个简单方便易用的智能指针shared_ptr和unique_ptr介绍完毕。其实都很简单,多用几次就会。