3.4.7 应用于工厂模式
工厂模式是一种创建型设计模式,这个模式包装了new操作符的使用,使对象的创建工作集中在工厂类或者工厂函数中,从而更容易适应变化,make_shared()就是工厂模式的一个很好的例子。
但由于C++(www.cppentry.com)不能高效地返回一个对象,在程序中编写自己的工厂类或者工厂函数时通常需要在堆上使用new动态分配一个对象,然后返回对象的指针。这种做法很不安全,因为用户很容易忘记对指针调用delete,存在资源泄漏的隐患。
使用shared_ptr可以解决这个问题,只需要修改工厂方法的接口,不再返回一个原始指针,而是返回一个被shared_ptr包装的智能指针,这样可以很好地保护系统资源,而且会更好地控制对接口的使用。
接下来我们使用代码来解释shared_ptr应用于工厂模式的用法,首先实现一个纯抽象基类,也就是接口类:
- class abstract //接口类定义
- {
- public:
- virtual void f() = 0;
- virtual void g() = 0;
- protected:
- virtual ~abstract(){} //注意这里
- };
注意abstract的析构函数,被定义为保护的,意味着除了它自己和它的子类,其他任何对象都无权调用delete来删除它。
然后我们再定义abstract的实现子类:
- class impl:public abstract
- {
- public:
- virtual void f()
- { cout << "class impl f" << endl; }
- virtual void g()
- { cout << "class impl g" << endl; }
- };
随后的工厂函数返回基类的shared_ptr:
- shared_ptr<abstract> create()
- { return shared_ptr<abstract>(new impl);}
这样我们就完成了全部工厂模式的实现,现在可以把这些组合起来:
- int main()
- {
- shared_ptr<abstract> p = create(); //工厂函数创建对象
- p->f(); //可以像普通指针一样使用
- p->g(); //不必担心资源
泄漏,shared_ptr会自动管理指针 - }
由于基类abstract的析构函数是保护的,所以用户不能做出任何对指针的破坏行为,即使是用get()获得了原始指针:
- abstract *q = p.get(); //正确
- delete q; //错误
这段代码不能通过编译,因为无法访问abstract的保护析构函数。
但这不是绝对的,使用"粗鲁"的方法也可以在shared_ptr外删除对象,因为impl的析构函数是公开的,所以:
- impl *q = (impl*)(p.get());
- delete q; //ok
这样就可以任意操作原本处于shared_ptr控制之下的原始指针了,但最好永远也不要这样做,因为这会使shared_ptr在析构时删除可能已经不存在的指针,引发未定义行为。