在C++中,动态内存的管理是通过一对运算符来完成的。new在动态内存中为对象分配空间并返回一个指向该内存对象的指针。
delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存!
为什么要用智能指针?
动态内存的使用很容易出现问题,因为确保在正确的时间释放内存是极其困难的。有时候我们会忘记释放内存这就会导致产生内存泄漏;为了更容易同时也更安全的使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象!
智能指针分哪几种?
shared_ptr : 允许多个指针指向一个对象
unique_ptr : 一个指针独占一个对象
weak_ptr : 弱引用,执行shared_ptr指针指向的对象。(可参见 本博客C++模块第9篇文章)
make_shared函数:在动态内存中创建一个对象并初始化它,返回指向该对象的一个shared_ptr指针。
#include<memory>
shared_ptr<int> p2 = make_shared<int>(42);
引用计数的问题:当用一个shared_ptr去初始化另一个shared_ptr,或者将它作为参数传递给一个函数,或者作为函数的返回值。这些操作都会增加引用计数!
一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象!
举个栗子:
shared_ptr<FOO> factory(T arg)
{
return shared_ptr<FOO>(arg);
}
void use_factory(T arg)
{
shared_ptr<FOO> p = factory(arg);
}
有上面的代码我们知道,当use_factory执行结束的时候,p就会被销毁,由于p所指向的对象只有一个引用,那么当p被销毁的时候,对应的对象的内存就会被释放!
再来看,我们将代码稍微变动一下
shared_ptr<FOO> factory(T arg)
{
return shared_ptr<FOO>(arg);
}
shared_ptr<FOO> use_factory(T arg)
{
shared_ptr<FOO> p = factory(arg);
return p;
}
当use_factory执行结束的时候,p就会被销毁,但是返回了p的副本,也就是增加了内存对象的引用计数,那么内存对象就不会被释放!
new在动态内存中为对象分配空间并返回一个指向该内存对象的指针,该指针是内置类型指针,我们在使用完内存对象的时候一定要记得显式地手动delete内存。
举个栗子:
FOO* factory(T arg)
{
return new FOO(arg);
}
void use_factory(T arg)
{
FOO* p = factory(arg);
}
有上面的代码我们知道,当use_factory执行结束的时候,p就会被销毁,但是它所指向的内存没有被释放!这就会导致所谓的内存泄露!因此我们需要显示的释放内存!或者返回一个指针副本指向这个内存对象!
FOO* factory(T arg)
{
return new FOO(arg);
}
void use_factory(T arg)
{
FOO* p = factory(arg);
delete p;
}
//或者
void use_factory(T arg)
{
FOO* p = factory(arg);
return p;
}
考虑到动态内存的管理很容易出现内存泄露或者把相同的指针delete多次。我们将new和shared_ptr结合使用!
但是new返回的是内置指针,因此只能够在对shared_ptr进行初始化的时候,直接使用值初始化!而不能够隐式转换!
shared_ptr<int> p(new int(42));
因此:一个用来初始化智能指针的普通指针必须是指向动态内存的!
那有没有相反的机制?即从智能指针退化到普通指针?
答案当然是有!智能指针类型定义了一个get()函数,返回一个内置指针,它指向智能指针所指向的对象!记住!!!不能够delete这个内置指针!因为智能指针还在引用这个内存对象!
当将一个内置指针所指向的内存绑定到一个智能指针上的时候,我们就不应该再使用内置指针去访问这块内存了。当我们需要向一个不能使用智能指针的代码传递一个内置指针,就可以使用get函数。
get是用来将指针的访问权限交给代码,只有在确认代码不会delete这个返回的内置指针的时候,才能够使用get!而且不能使用get返回的指针去初始化另一个智能指针!
智能指针和异常
当我们使用某个代码块中使用了智能指针,且在代码块执行完成之前抛出了异常,且没有进行捕获处理,在程序退出之后,智能指针所指向的内存是可以被销毁回收的!但是如果是内置的指针的话,那么在delete之前抛出异常导致程序退出的话,那么这部分内存无法被回收了,也即产生了内存泄露。
其它的shared_ptr的操作:
1)reset()
shared_ptr<int> p = make_shared<int>(42);
p.reset(new int(42)); //p指向新的对象
2)unique() 用来判断是否只有自身这一个智能指针指向这个内存对象!
if(!p.unique())
p.reset(new string(*p)); //拷贝对象,指向新的拷贝对象!
*P += “hello”; //如果是唯一的,改变对象的值
unique_ptr 一个指针独占一个对象!和new结合使用的时候,初始化必须要直接初始化!
由于unique_ptr独享指向的内存对象!因此unique_ptr不支持普通的拷贝或赋值操作!
我们使用 release()和reset()将指针的所有权转移到另一个unique_ptr上。
unique_ptr<int>p1(new int(1024));
unique_ptr<int>p2(p1.release())
unique_ptr<int>p3(new int(2048));
p2.reset(p3.release());
release()返回的内置指针常用来初始化另外一个智能指针或者给另外一个智能指针赋值!如果我们不用一个智能指针来保存返回的指针,我们就要记得手动delete这个指针!
还可以向unique_ptr传递删除器
unique_ptr<objT,delT> p (new objT , fcn);
weak_ptr:是一种不控制所指向对象的智能指针,它指向一个由shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个shared_ptr被销毁,那么对象就会被释放。即使有weak_ptr指向对象,对象也会被释放!
在创建一个weak_ptr的时候,要用一个shared_ptr来初始化!
不能使用weak_ptr直接访问对象,要使用lock()函数。此函数检查weak_ptr指向的内存对象是否存在!如果存在返回一个shared_ptr。
shared_ptr<int> p = make_shared<int>(1024);
weak_ptr<int>wp(p);
if(shared<int> np = wp.lock()){} //存在则返回一个shared_ptr