shared_ptr
智能指针是指向动态分配(堆)对象指针,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每次使用它,内部的引用计数加1,每次析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。
每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候, 内存才会被释放。
可以通过构造函数、赋值函数或者make_shared函数初始化智能指针。
shared_ptr基于”引用计数”模型实现,多个shared_ptr可指向同一个动态对象,并维护一个共享的引用计数器,记录了引用同一对象的shared_ptr实例的数量。当最后一个指向动态对象的shared_ptr销毁时,会自动销毁其所指对象(通过delete操作符)。
shared_ptr的默认能力是管理动态内存,但支持自定义的Deleter以实现个性化的资源释放动作。
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。当要用make_shared时,必须指定想要创建的对象的类型,定义方式与模板类相同。在函数名之后跟一个尖括号,在其中给出类型。例如,调用make_shared<string>时传递的参数必须与string的某个构造函数相匹配。如果不传递任何参数,对象就会进行值初始化。
通常用auto定义一个对象来保存make_shared的结果。
当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其它shared_ptr指向相同的对象。
可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数(reference count)。无论何时拷贝一个shared_ptr,计数器都会递增。例如,当用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值时,它所关联的计数器就会递增。当给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的shared_ptr离开其作用域)时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。
当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象。它是通过另一个特殊的成员函数析构函数(destructor)来完成销毁工作的。类似于构造函数,每个类都有一个析构函数。就像构造函数控制初始化一样,析构函数控制此类型的对象销毁时做什么操作。shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。
如果将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素。
使用shared_ptr注意事项:
(1)、不要把一个原生指针给多个shared_ptr管理;
(2)、不要把this指针给shared_ptr;
(3)、不要在函数实参里创建shared_ptr;
(4)、不要不加思考地把指针替换为shared_ptr来防止内存泄漏,shared_ptr并不是万能的,而且使用它们的话也是需要一定的开销的;
(5)、环状的链式结构shared_ptr将会导致内存泄漏(可以结合weak_ptr来解决);
(6)、共享拥有权的对象一般比限定作用域的对象生存更久,从而将导致更高的平均资源使用时间;
(7)、在多线程环境中使用共享指针的代价非常大,这是因为你需要避免关于引用计数的数据竞争;
(8)、共享对象的析构器不会在预期的时间执行;
(9)、不使用相同的内置指针值初始化(或reset)多个智能指针;
(10)、不delete get()返回的指针;
(11)、不使用get()初始化或reset另一个智能指针;
(12)、如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了;
(13)、如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。
shared_ptr的线程安全性
shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。根据文档,shared_ptr 的线程安全级别和内建类型、标准库容器、string 一样,即:
- 一个 shared_ptr 实体可被多个线程同时读取;
- 两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
- 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。
shared_ptr<int> sp(new int(10)); //一个指向整数的shared_ptr
assert(sp.unique()); //现在shared_ptr是指针的唯一持有者
shared_ptr<int> sp2 = sp; //第二个shared_ptr,拷贝构造函数
assert(sp == sp2 && sp.use_count() == 2); //两个shared_ptr相等,指向同一个对象,引用计数为2
*sp2 = 100; //使用解引用操作符修改被指对象
assert(*sp == 100); //另一个shared_ptr也同时被修改
sp.reset(); //停止shared_ptr的使用
assert(!sp);
class shared //一个拥有shared_ptr的类
{
private:
shared_ptr<int> p; //shared_ptr成员变量
public:
shared(shared_ptr<int> p_):p(p_){} //构造函数初始化shared_ptr
void print() //输出shared_ptr的引用计数和指向的值
{ cout << "count:" << p.use_count()
<< "v =" <<*p << endl;
}
};
void print_func(shared_ptr<int> p) //使用shared_ptr作为函数参数
{
//同样输出shared_ptr的引用计数和指向的值
cout << "count:" << p.use_count()
<< " v=" <<*p << endl; }
int main()
{
shared_ptr<int> p(new int(100));
shared s1(p), s2(p); //构造两个自定义类
s1.print();
s2.print();
*p = 20; //修改shared_ptr所指的值
print_func(p);
s1.print();
}
上一篇: Apache Httpd 安装 AH00558错误
下一篇: nginx开启gzip压缩