一,智能指针模板
1.1,什么是智能指针?
智能指针是行为类似于指针的类对象,使用智能指针可帮助管理动态分配的内存与使用new创建的对象。
1.2,智能指针模板
C++中提供的智能指针模板有auto_ptr、unique_ptr、shared_ptr和weak_ptr。可以将new获得的地址赋给智能指针对象。当智能指针过期时,其析构函数将使用delete来释放内存。因此,如果将new返回的地址赋给智能指针对象,将无需记住稍后释放这些内存,当智能指针过期时,这些内存会自动被释放。智能指针对象的很多方面都类似于常规指针,例如,可以对它执行解除引用操作(*p)、访问结构成员(p->data)、将它赋给相同类型的智能指针。但是将智能指针对象赋值个另一个智能指针对象,将引发一些问题。
1.3,智能指针的应用
要创建智能指针对象,必须包含头文件memory。例如,模板auto_ptr包含如下构造函数
template<class X>
class auto_ptr{
public:
explicit auto_ptr(X *p = 0) throw();
...
};
1,使用智能指针来管理动态分配的内存
下面是使用智能指针的例子
auto_ptr<int> pInt(new int);
下面是不使用智能指针的例子
int *p = new int;
...
delete p;
上面的指针变量p是一个普通的指针,当p的生命周期结束时,指针p分配的内存会被释放,但是p指向的内存空间不会被释放,因此,要手动调用delete,释放掉动态分配的内存空间。但pInt是一个智能指针,它是一个对象,当它的生命周期结束时,会调用它的析构函数,在析构函数中会释放掉动态分配的内存。
2,使用智能指针来管理用new创建的对象
class Resource{
public:
Resource(){
cout<<"object created."<<endl;
}
~Resource(){
cout<<"object delete."<<endl;
}
};
int main(){
auto_ptr<Resource> p1(new Resource);
shared_ptr<Resource> p2(new Resource);
unique_ptr<Resource> p3(new Resource);
}
输出结果
object created.
object delete.
object created.
object delete.
object created.
object delete.
Process returned 0 (0x0) execution time : 0.039 sPress any key to continue.
注意:
智能指针不能用来管理非堆内存,例如:
Resource res;
auto_ptr<Resource> p1(res);
p1过期时,程序将把delete运算符用于非堆内存,这是错误的。
1.4,智能指针赋值与复制操作带来的问题
(1),智能指针赋值带来的问题
auto_ptr<Resource> p1(new Resource);
auto_ptr<Resource> p2;
p2 = p1;
如果p1与p2是常规指针,则两个指针将指向同一个Resource对象。但是对于智能指针来说这是不能接受的,因为程序将试图删除同一个对象两次,一次是p1过期时,另一次是p2过期时。要避免这种问题,方法有多种:
- 建立所有权概念:对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后,让赋值操作转让所有权。这就是用于auto_ptr与unique_ptr的策略,但unique_ptr的策略更加严格。
- 使用引用计数:创建更高智能的指针,跟踪引用特定对象的智能指针数,这称为引用计数。例如,赋值时,引用计数加1,而指针过期时,计数将减1。仅当最后一个指针过期时,才调用delete,这是shared_ptr采用的策略。
(2),错误使用auto_ptr带来的问题
auto_ptr<string> p1(new string("hello world."));
auto_ptr<string> p2;
p2 = p1;
cout<<*p2<<endl;
cout<<*p1<<endl;
上面的程序在运行时会出错,出错的原因是,赋值语句p2 = p1将所有权从p1转到p2,这导致p1不再引用该字符串。在auto_ptr类型的指针放弃对象的所有权之后,便可能使用它来访问该对象,却发现这个智能指针是空的,所以会导致上面的问题。unique_ptr与auto_ptr都是采用所有权模型,如果用unique_ptr代替auto_ptr也会出错。但使用unique_ptr时,程序不会等到运行期崩溃,而在编译器发现赋值操作p2 = p1,将报错。
(3),使用shared_ptr代替auto_ptr解决上面的问题
shared_ptr<string> p1(new string("hello world."));
shared_ptr<string> p2;
p2 = p1;
cout<<*p2<<endl;
cout<<*p1<<endl;
上面的程序正常运行,这次p1与p2指向同一个对象,对象的引用计数由1增加到2。在程序末尾,p2的生命周期结束时,会调用它的析构函数,此时对象的引用计数降低为1。然后p1的生命周期结束,会调用它的析构函数,此时对象的引用计数降低为0,释放掉为对象分配的内存空间。