智能指针的概念
在传统C++中,我们用new和delete去对资源进行申请和释放,而C++11引入了智能指针的概念,让程序员不再需要关心手动释放内存。
RAII: Resource Acquisition Is initialization(资源获取即初始化). RAII的核心思想是将资源或者状态与对象的生命周期绑定,通过C++的语言机制,实现资源和状态的安全管理。理解和使用RAII能使软件设计更清晰,代码更健壮。
智能指针使得自动、异常安全的对象生存期管理可行。
智能指针的本质是类模板。
C++共有四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被c++11弃用。
1.auto_ptr
auto_ptr 可以通过将普通指针作为参数传入智能指针的构造函数实现绑定。当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。
auto_ptr是C++98标准库中的智能指针模板类 ,在C++11已经被废除, 可以用unique_ptr取代,功能上是相同的,unique_ptr较auto_ptr 而言,提升了安全性(没有浅拷贝),增加了特性(delete析构)和对数组的支持。
其功能如下:
- 支持拷贝构造 (危险)
- 支持赋值拷贝 (危险)
- 支持operator->/operator*解引用
- 支持指针变量重置
- 保证指针持有者唯一
auto_ptr的构造参数可以是一个指针,或者是另外一个auto_ptr对象。注意:当一个新的auto_ptr获取了内部指针的所有权之后,之前的拥有者会释放其所有权,即被释放。
构造:
std::auto_ptr<int> p(new int(3));
拷贝构造(剪切):
auto_ptr< string > p1 (new string( "hellow" ) );
auto_ptr< string > p2( p1); //利用p1来构造p2,p1被释放(null)
因为一块动态内存只能由一个智能指针独享,所以在调用拷贝构造或赋值运算符时都会发生拥有权转移的过程。在此拷贝构造过程中,p1将失去对字符串内存的所有权(p1为空),而p2将其获得。对象销毁时,p2负责内存的自动销毁。
赋值(剪切)
//利用已经存在的智能指针来构造新的智能指针
auto_ptr< int > p1( new int( 1024 ) );
auto_ptr< int > p2( new int( 2048 ) );
p1 = p2;
在赋值之前,由p1 指向的对象被删除。赋值之后,p1 拥有int 型对象的所有权。该对象值为2048。 p2指向null。
auto_ptr的缺点:
- 不能有两个auto_ptr对象拥有同一个内部指针的所有权,因为有可能在某个时机,两者均会尝试析构这个内部指针。
- 当两个auto_ptr对象之间发生赋值操作时,内部指针被拥有的所有权会发生转移,这意味着这个赋值的右者对象会丧失该所有权,不再指向这个内部指针(其会被设置成null)。-- 可以理解为剪切
- auto_ptr不支持传入deleter,所以只能支持单对象(delete object)
- auto_ptr不支持容器。
有问题的使用场景一:函数传参
auto_ptr拷贝赋值时是所有权转移,函数传参会发生一个拷贝,p1拷贝给p,导致其内部指针的所有权被转移给了p,导致p1指向null。
void foo(std::auto_ptr<int>p)
{
cout<<*p<<endl;
}
int main()
{
std::auto_ptr<int>p1 = std::auto_ptr<int>(new int(1024));
foo(p1); // p1失去了内部指针的所有权,变为null.
cout<<*p1<<endl;//访问空指针,程序crash
}
有问题的使用场景二:把auto_ptr放到容器中
应绝对避免把auto_ptr放到容器中,(unique_ptr)auto_ptr拷贝赋值时是所有权转移,而在容器中可能会产生临时对象拷贝赋值,临时对象获得所有权之后就析构了释放了对象。并且很难避免STL内部对容器中的元素实现赋值,这样便会使容器中多个元素被置位NULL
std::vector<std::auto_ptr<int> arr;
std::auto_ptr<int>a1(new int(1024));
arr.push_back(a1);// 编译出错。binding reference of type 'std::auto_ptr<int>&' to 'const std::auto_ptr<int>' discards qualifiers。
std::cout << peoples[5]->get_name() << std::endl;
auto_ptr的析构函数调用的是delete不是delete [],所以不能使用智能指针数组。
判断一个auto_ptr是否为空不能使用if(ptest == NULL),应该使用if(ptest.get() == NULL)
常用成员函数:
- get函数:
返回智能指针指向对象的指针. - reset函数:
reset函数提供了默认参数,功能是取消auto_ptr对原对象的控制权,并选择性提供一个新的对象控制权. - realease函数:
realease实现的功能就是将原智能指针的控制权取消,不释放原始指针.