auto_ptr
由于auto_ptr被销毁时会自动删除它所之物,所以一定要注意别让多个auto_ptr指向同一个对象。如果真的是那样,对象会被删除一次以上,而那会是你的程序搭上“未定义行为”的快速列车上,为了预防auto_ptrs,有一个不同寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,从而复制所得的指针将取得资源的唯一拥有权
这是一个诡异的复制行为,附加上其底层条件:“受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它”,意味着auto_ptrs并非管理动态资源的神兵利器。
shared_ptr
auto_ptr的替代方案是“引用计数智能指针(reference_counting smart pointer;RCSP)”。RCSP持续追踪有多少个对象指向某笔资源,并在无人指向它时自动删除该资源。
多个shared_ptr对象可能拥有同一个对象。当以下任一情况发生时,对象将被销毁并释放其内存:
- 最后剩下的shared_ptr拥有该对象的对象被销毁;
- 最后一个shared_ptr拥有该对象的人通过operator=或reset()分配了另一个指针。
在典型的实现中,shared_ptr只保存两个指针:
- 存储的指针(由get()返回的指针);
- 指向控制块的指针。
控制块是一个动态分配的对象,它包含:
- either a pointer to the managed object or the managed object itself;
- 删除器;
- 分配器;
- the number of shared_ptr that own the object;
- the number of weak_ptr that own the object;
当shared_ptr通过调用std::make_shared或std::allocate_shared创建时,控制块和托管对象的内存都是使用单个分配创建的。托管对象在控制块的数据成员中就地构造。
当shared_ptr通过shared_ptr构造函数之一创建时,必须分别分配托管对象和控制块。在这种情况下,控制块存储一个指向被管理对象的指针。
shared_ptr直接持有的指针是get()返回的指针,而控制块持有的指针/对象是当共享所有者的数量达到零时将被删除的指针/对象。这些指针不一定相等。
shared_ptr的destructor减少控制块的共享所有者的数量。如果该计数器达到零,则控制块调用destructor of the managed object。在std::weak_ptr计数器也达到零 之前,控制块不会自行释放。
weak_ptr
weak_ptr持有对std::shared_ptr管理的对象的非拥有(“弱”)引用。必须将其转换为shared_ptr才能访问引用的对象。
weak_ptr建模临时所有权:当一个对象只有在它存在时才需要访问,并且它可能被其他人随时删除,weak_ptr用于跟踪该对象,并将其转换为std::shared_ptr以承担临时所有权。如果此时原始std::shared_ptr被销毁,则对象的生命周期会延长,直到临时std::shared_ptr也被销毁。
weak_ptr的另一个用途是打破由std::shared_ptr管理的对象形成的引用循环。如果这样的循环是孤立的(即循环中没有外部共享指针),则shared_ptr引用计数不能达到零并且内存泄漏。为了防止这种情况,可以将循环中的指针之一设置为 weak。
与shared_ptr一样,weak_ptr存储两个指针 的典型实现:
- 指向控制块的指针
- shared_ptr的存储
当 shared_ptr 析构并释放共享资源的时候,只要 weak_ptr 对象还存在,控制块就会保留,weak_ptr 可以通过控制块观察到对象是否存活。
循环引用问题
class parent;
class children;
class parent
{
public:
~parent() { std::cout << "destroying parent\n"; }
public:
//weak_ptr<children> children;
shared_ptr<children> children;
};
class children
{
public:
~children() { std::cout << "destroying children\n"; }
public:
shared_ptr<parent> parent;
//weak_ptr<parent> parent;
};
void test()
{
shared_ptr<parent> father(new parent);
shared_ptr<children> son(new children);
father->children = son;
cout << son.use_count() << endl;
son->parent = father;
cout << father.use_count() << endl;
}
void main()
{
std::cout << "begin test...\n";
test();
std::cout << "end test.\n";
cin.get();
}
parent 类中有指向children 类的shared_ptr智能指针,children 类中有指向parent类的shared_ptr 智能指针,他们相互指向会构成shared_ptr 的循环引用。
利用weak_ptr就可以解决循环引用的问题,因为weak_ptr是弱引用型指针,是用来监视shared_ptr的,不会使引用计数增加,但是它不管理shared_ptr内部的指针,也就是数据指针,它是用来监视shared_ptr生命周期的,通过它的构造不增加引用计数,析构不减少引用计数这一点,从而解决了循环引用的问题。
class parent
{
public:
~parent() { std::cout << "destroying parent\n"; }
public:
weak_ptr<children> children;
//shared_ptr<children> children;
};
class children
{
public:
~children() { std::cout << "destroying children\n"; }
public:
shared_ptr<parent> parent;
};
void test()
{
shared_ptr<parent> father(new parent);
shared_ptr<children> son(new children);
father->children = son;
cout << son.use_count() << endl;
son->parent = father;
cout << father.use_count() << endl;
}
void main()
{
std::cout << "begin test...\n";
test();
std::cout << "end test.\n";
cin.get();
}