Boost源码剖析--<boost/smart_ptr/scoped_ptr.hpp>
By 马冬亮(凝霜 Loki)
一个人的战争(http://blog.csdn.net/MDL13412)
头文件:<boost/scoped_ptr.hpp>
定位:
对于需要在一定范围内精确控制生命周期,并且不需要共享所有权或所有权转让语义的对象,提供内存的托管。由于不允许拷贝操作,可以在一定程度上防止误操作,较shared_ptr和std::auto_ptr更为安全。
分析:
scoped_ptr提供了RAII(Resource Acquisition Is Initialization[资源获取就是初始化])语义,这是良好的C++编程风格,可实现资源的自动回收,并且在scoped_ptr的生命周期内,其托管类型的资源始终保持有效,不需要检测资源有效性。
由于实现简单,所以其运行期开销非常小,拥有接近裸指针的相应速度,接近裸指针的内存开销,且提供了更丰富的控制语义。
注意:
只能托管指向动态分配对象的指针(由new T分配)。
使用reset后,其托管的资源被释放。
不能用于STL容器中,如果需要此项支持,应该使用shared_ptr。
不能指向一个动态分配的数组指针,会导致释放错误,此需求应该使用scoped_array。
需要满足智能指针的通用要求,见下文。
Common Requirements:
These smart pointer class templates have a template parameter, T, which specifies the type of the object pointed to by the smart pointer. The behavior of the smart pointer templates is undefined if the destructor or operator delete for objects of type T throw exceptions.
T may be an incomplete type at the point of smart pointer declaration. Unless otherwise specified, it is required that T be a complete type at points of smart pointer instantiation. Implementations are required to diagnose (treat as an error) all violations of this requirement, including deletion of an incomplete type. See the description of the checked_delete function template.
Note that shared_ptr does not have this restriction, as most of its member functions do not require T to be a complete type.
[上述Common Requirements小节摘自Boost文档]
设计哲学:
使用scoped_ptr可以让阅读你代码的程序员知道你的意图--仅仅在当前作用域中使用RAII机制,并且放弃转让所有权。
避免std::auto_ptr在转让所有权语义上的陷阱,同时也让程序员知道此资源不可转让。
常用于Handle/Body(pImpl)惯用法,简化资源管理。
不提供release()成员是为了保证scoped_ptr的所有权不会被转移,防止引起歧义。
源码剖析:
#ifndef BOOST_SMART_PTR_SCOPED_PTR_HPP_INCLUDED
#define BOOST_SMART_PTR_SCOPED_PTR_HPP_INCLUDED
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
// Copyright (c) 2001, 2002 Peter Dimov
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// http://www.boost.org/libs/smart_ptr/scoped_ptr.htm
//
// Comment By: 凝霜
// E-mail: mdl2009@vip.qq.com
// Blog: http://blog.csdn.net/mdl13412
#include <boost/assert.hpp>
#include <boost/checked_delete.hpp>
#include <boost/detail/workaround.hpp>
#ifndef BOOST_NO_AUTO_PTR
# include <memory> // for std::auto_ptr
#endif
namespace boost
{
// Debug hooks
// 这里定义的是用于Boost库开发过程中调试和测试时的回调函数。
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
void sp_scalar_constructor_hook(void * p);
void sp_scalar_destructor_hook(void * p);
#endif
// scoped_ptr mimics a built-in pointer except that it guarantees deletion
// of the object pointed to, either on destruction of the scoped_ptr or via
// an explicit reset(). scoped_ptr is a simple solution for simple needs;
// use shared_ptr or std::auto_ptr if your needs are more complex.
template<class T> class scoped_ptr // noncopyable
{
private:
T * px; // 指向托管资源
// 禁止所有权的转让
scoped_ptr(scoped_ptr const &);
scoped_ptr & operator=(scoped_ptr const &);
typedef scoped_ptr<T> this_type;
// 注意:托管的资源只能被一个scoped_ptr所有,否则会导致资源的意外释放。
void operator==( scoped_ptr const& ) const;
void operator!=( scoped_ptr const& ) const;
public:
typedef T element_type;
// explicit的作用是防止隐式转换
// 只有单参数的构造函数才需要此关键字
explicit scoped_ptr( T * p = 0 ): px( p ) // never throws
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_constructor_hook( px );
#endif
}
#ifndef BOOST_NO_AUTO_PTR
// 由std::auto_ptr构造scoped_ptr,并释放掉std::auto_ptr对资源的所有权。
explicit scoped_ptr( std::auto_ptr<T> p ): px( p.release() ) // never throws
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_constructor_hook( px );
#endif
}
#endif
~scoped_ptr() // never throws
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_destructor_hook( px );
#endif
// 检测px是否具有完全类型,防止释放错误。
boost::checked_delete( px );
}
void reset(T * p = 0) // never throws
{
BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
// 构造一个临时对象,用于“转让”原来托管的资源。
// 这个临时对象在交换完成后,生命周期结束,自动释放原来的资源。
this_type(p).swap(*this);
}
T & operator*() const // never throws
{
BOOST_ASSERT( px != 0 );
return *px;
}
// 恩,重载->操作符,大家的直觉可能会认为会返回T &,:-)
// 但是C++自身的语义决定了,这个操作需要返回T *然后编译器会自动去处理相关的事情。
T * operator->() const // never throws
{
BOOST_ASSERT( px != 0 );
return px;
}
// 这个是提供给一些需要使用裸指针的场合,例如strcpy()等。
T * get() const // never throws
{
return px;
}
// 这个其实就是一个operator unspecified_bool_type() const的重载
// 用于if (sp) 或者 if (!sp)一类的boolean操作
// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>
// 交换托管资源
void swap(scoped_ptr & b) // never throws
{
T * tmp = b.px;
b.px = px;
px = tmp;
}
};
// 将全局的swap特化,防止调用std::swap产生错误的行为。
template<class T> inline void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) // never throws
{
a.swap(b);
}
// get_pointer(p) is a generic way to say p.get()
template<class T> inline T * get_pointer(scoped_ptr<T> const & p)
{
return p.get();
}
} // namespace boost
#endif // #ifndef BOOST_SMART_PTR_SCOPED_PTR_HPP_INCLUDED
实例:
#include <iostream>
#include <cstdlib>
#include <boost/smart_ptr/scoped_ptr.hpp>
using namespace std;
struct Dummy
{
int id_;
explicit Dummy(int id) : id_(id) { }
~Dummy() { cout << "~Dummy() id: " << id_ << endl; }
void foo() { cout << "foo() id: " << id_ << endl; }
};
int main(int argc, char** argv)
{
{
::boost::scoped_ptr<Dummy> sp_1(new Dummy(1));
::boost::scoped_ptr<Dummy> sp_2(new Dummy(2));
sp_1->foo();
sp_2->foo();
}
cout << "前面托管的资源超出作用域,在这句话前被释放" << endl;
return 0;
}
foo() id: 1
foo() id: 2
~Dummy() id: 2
~Dummy() id: 1
前面托管的资源超出作用域,在这句话前被释放
运行 成功 (总计时间: 251毫秒)
大家可以看到,在scoped_ptr超出其生命周期后,其托管的资源被自动释放。
下面给大家一个pImpl的实例,这是C++中最常用的惯用法。
// Dummy.h
#ifndef DUMMY_H
#define DUMMY_H
#include <boost/scoped_ptr.hpp>
class Dummy
{
public:
Dummy();
~Dummy();
private:
class ImplClass; // 这里只有声明,没有实现
::boost::scoped_ptr<ImplClass> impl_;
};
#endif /* DUMMY_H */
// Dummy.cpp
#include "Dummy.h"
#include <iostream>
class Dummy::ImplClass
{
public:
~ImplClass() { std::cout << "~ImplClass()" << std::endl; }
};
Dummy::Dummy() : impl_(new ImplClass) { }
Dummy::~Dummy() { }
// main.cpp
#include <cstdlib>
#include "Dummy.h"
int main(int argc, char** argv)
{
{
Dummy dummy;
}
return 0;
}
~ImplClass()
运行 成功 (总计时间: 242毫秒)
总结:
scoped_ptr提供了RAII机制,是良好的C++编程风格。
scoped_ptr可以在绝大多数情况下取代std::auto_ptr,而余下的情况则可以使用shared_ptr。
不可以用作STL容器,因为其不支持copy语义。
scoped_ptr比shared_ptr有着更强的暗示,暗示其作用域与对象所有权,更好的帮助其他程序员维护既有代码。
scoped_ptr非常适合实现pImpl惯用法。
scoped_ptr不可以托管动态分配的数组,请使用scoped_array。