(1)reset操作
std::shared_ptr的reset函数并不一定将裸指针释放,一样需要先判断引用计数是否归零。不过和其他智能指针一样,reset()函数可以带一个新的裸指针作入参。
(2)管理辅助
unique()判断一个shared_ptr当前是否在和别的对象共享裸指针。返回真代表这个指针独立拥有裸指针。
use_count()则返回共享引用的个数。这两个函数通常不会用于实际业务,仅作为程序员写程序时做一些简单的观察及测试。
(3)管理多态指针
面向对象设计中,经常出现声明为基类的指针,实际指向派生类。shared_ptr可以良好地处理这种情况。
下例演示一个基类B,有两个派生类D1和D2。
基类作为一个接口,它拥有纯虚函数,特别是它拥有一个“虚析构”。后者是shared_ptr将来可以正确删除指针,释放内存的重要保障。
struct B
{
virtual void prn() = 0; ///纯虚函数
//虚析构,shared_ptr将来可以正确删除指针,释放内存的中药保障
virtual ~B(){}
};
struct D1 : public B
{
void prn() override {cout << 1 << endl;}
~D1() override {cout << "~D1" << endl;}
};
struct D2 : public B
{
void prn() override {cout << 2 << endl;}
~D2() override {cout << "~D2" << endl;}
};
然后我们声明两个管理基类指针的shared_ptr变量。请注意二者的声明类型(也称静态类型)都是“shared_ptr <B>”,但实际创建的分别是派生类D2和D2:
void test()
{
std::shared_ptr <B> sb1(new D1);
std::shared_ptr <B> sb2(new D2);
sb1->prn();
sb2->prn();
}
调用test()函数,观察sb1和sb2,二者和裸指针一样,完美支持面向对象的多态特性,最终释放所调用的析构函数,也完全正确。
(4)类系指针转换
在类系中使用裸指针,可通过dynamic_cast<T>()或static_cast<T>()在基类和派生类指针减做转换。继续上例中的B、D1、D2三者:
B* pb = new D1;
D1* pd = dynamic_cast <D1*>(pb);
既然pb 实际上是指向一个D1类对象,所以通过dynamic_cast转换,可以将pb向下转换为“D1*”。甚至可以尝试将pb向下转换到“D2*”,当然这样只会得到一个nullptr,但代码合法:
if(D2* pd2 = dynamic_cast <D2*>(pb))
{
///这里的代码永远不会执行,因为pd2必为nullptr
cout << "--------" << endl;
}
dynamic_cast、static_cast、const_cast等无法直接作用到智能指针身上:
std::shared_ptr <B> pb(new D1);
typedef std::shared_ptr <D1> D1Ptr;
D1Ptr pd (dynamic_cast <D1Ptr> (pb));
//以下这样更不行
D1Ptr pd2 (dynamic_cast <D1*> (pb));
编译失败,因为在编译看来,shared_ptr<B> 和 shared_ptr <D1>没有任何关系。这很合清理,尽管B和D1是基类和派生类的关系,但将二者套入shared_ptr<T>之后,不能说shared_ptr<B>和shared_ptr<D1>也有派生关系。
怎么解决呢?通过只能指针的get()成员得到裸指针自然就可以继续使用dynamic_cast/const_cast/static_cast等,但这样做很危险,很容易发生前面所说的“不作不会死”的情况。
为此C++11在库中提供了标准实现,又是三个很丑的转换操作模版:
template <class T, class U>
std::shared_ptr <T>
static_pointer_cast(const std::shared_ptr <U>& r);
template <class T, class U>
std::shared_ptr <T>
dynamic_pointer_cast(const std::shared_ptr <U>& r);
template <class T, class U>
std::shared_ptr <T>
const_pointer_cast(const std::shared_ptr <U>& r);
名字中间都插入了“pointer”,不过其实只用于处理shared_ptr类型,作用和各自去除pointer的转换操作类似:“static”版和“dynamic”版都可用于基类和派生类指针之间的双向转换,前者不作正确性检查,后者会在运行时尝试检查。“const”则用于处理常量和非常量指针(指智能指针所拥有的裸指针)之间的强制转换。
使用示例:
std::shared_ptr <B> pb(new D1);
std::shared_ptr <D1> pd(dynamic_pointer_cast <D1> (pb));
pd->prn();
注意,套入XXX_point_cast<T>中的类型,并不是shared_ptr<D1>,而只是D1。至于为什么不是“D1 *”,因为名称中的“point”已经说明所要转换的必须是指针,没必要再添加多余的“*”符号了。