最近在看陈硕的《Linux多线程服务端编程:使用muduo C++网络库》,第一章中重点讲了多线程下指针的处理问题,其中重点是 shared_ptr 与 weak_ptr, 其中还提到在内存泄露和重复释放中使用 scoped_ptr。 上三篇学习了 shared_ptr 的基本用法 ( shared_ptr类模版 ,shared_ptr类模板(2)) 和 weak_ptr 的基本用法 ( waek_ptr类模版 ) ,今天继续学习 scoped_ptr ,以下是对 scoped_ptr class template 的部分中文翻译。
基本用法
保证销毁
scoped_ptr 保存了动态分配对象的指针 (C++ 的 new 操作符)。当 scoped_ptr 被析构或者 reset 的时候,被指向的对象保证被销毁。
不可复制
scoped_ptr 模版提供了基本的“资源只需要初始化”的机制,没有共享所有权或者传递所有权的语义,只是在当前的范围保留所有权 (noncopyable),因为它不能拷贝,所以对于不应该被复制的指针,比 shared_ptr 或者 std::auto_ptr 要安全。
效率与空间
因为 scoped_ptr 是简单的,它的操作与内置的指针一样快,占据的空间也不多。
scoped_ptr 与 C++ 标准库
scoped_ptr 不能用在 C++ 标准库容器中,请使用需要需要智能指针来用于容器,请使用 shared_ptr。
数组指针
scoped_ptr 不能正确地保存动态分配的数组的指针。scoped_array 提供了这用途。
T 的要求
scoped_ptr 的模版参数 T 是指向对象的类型,需要满足智能指针的 通常要求:
这些智能指针类模版由一个模版参数 T , 指定了被智能指针指向的对象的类型。如果 T 类型对象的 析构函数 或者 operator delete 抛出异常,那么智能指针模版的行为无定义。
在智能指针声明的时候,T 可能是不完整类型。除非特别说明,当智能指针实例化的时候,需要 T 是完整类型。实现需要检查所有违反该需求的情况,包括不完整类型的销毁操作。->查看 [checked delete] 函数模版的描述。
注意 shared_ptr 没有这个限制,因为它的大多数成员函数不需要 T 是完整类型。
注意析构时,scoped_ptr 需要 T 是完整类型,而 shared_ptr 不需要。
Synopsis
1 | namespace boost { |
Members
element_type
1 | typedef T element_type; |
存储指针的类型。
构造函数
1 | explicit scoped_ptr(T * p = 0); // never throws |
构建一个 scopeda_ptr, 存储了 p 的拷贝 (p 必须由 new 表达式分配或者为 0)。T 不需要是完整类型,看智能指针的 通常要求。
析构函数
1 | ~scoped_ptr(); // never throws |
析构保存的指针指向的对象,和 delete this->get() 效果一样。
不抛出异常使用了 delete 对象时不抛出异常的要求。看智能指针的 通常要求。
reset
1 | void reset(T * p = 0); // never throws |
析构保存的指针指向的对象,再保存 p 的拷贝 (p 必须由 new 表达式分配或者为 0)。不抛出异常使用了 delete 对象时不抛出异常的要求。看智能指针的 通常要求。
indirection
1 | T & operator*() const; // never throws |
返回保存的指针指向对象的引用,如果保存的指针为 0,那么行为将是未定义的。
1 | T * operator->() const; // never throws |
返回保存的指针,如果保存的指针为 0,那么行为将是未定义的。
get
1 | T * get() const; // never throws |
返回保存的指针,T 必须为完整类型。看智能指针的 通常要求。
conversions
1 | operator unspecified-bool-type () const; // never throws |
返回未指定的值,当用于 boolean 上下文时,等同于 get() != 0。
swap
1 | void swap(scoped_ptr & b); // never throws |
交换两个智能指针的内容,T 不需要为完整类型。看智能指针的 通常要求。
Free Functions
1 | template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws |
等同于 a.swap(b)。符合 std::swap 接口,提供了泛型编程的支持。
Example
下面这个例子展示了 scoped_ptr 的使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// scoped_ptr_example1.cpp
struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } };
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; }
int add_one() { return ++*ptr; }
};
int main()
{
boost::scoped_ptr<Shoe> x(new Shoe);
MyClass my_instance;
std::cout << my_instance.add_one() << '\n';
std::cout << my_instance.add_one() << '\n';
}
编译运行1
2
3
4
5
6g++ -g scoped_ptr_example1.cpp -o scoped_ptr_example1
./scoped_ptr_example1
1
2
Buckle my shoe
Rationale
使用 scoped_ptr 的主要原因是它 比 auto_ptr 更加直观,更加便于维护。
Handle/Body Idiom
scoped_ptr 的一个通常用法是实现 handle/body (也称为 pimpl) idiom,它能避免再头文件中暴露具体实现。
scoped_ptr_example_test.cpp 例子包含了一个使用 scoped_ptr<> 来隐藏实现的头文件 scoped_ptr_example.hpp 和 包含了需要完整类型的成员函数实例化 scoped_ptr_example.cpp。
1 | // scoped_ptr_example.hpp |
1 | // scoped_ptr_example.cpp |
1 | // Boost scoped_ptr_example_test main program -------------------------------// |
编译运行1
2
3
4
5g++ -g scoped_ptr_example.cpp scoped_ptr_example_test.cpp -o scoped_ptr_example_test
./scoped_ptr_example_test
did something
destroying implementation