scoped_ptr类模版

最近在看陈硕的《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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace boost {

template<class T> class scoped_ptr : noncopyable {

public:
typedef T element_type;

explicit scoped_ptr(T * p = 0); // never throws
~scoped_ptr(); // never throws

void reset(T * p = 0); // never throws

T & operator*() const; // never throws
T * operator->() const; // never throws
T * get() const; // never throws

operator unspecified-bool-type() const; // never throws

void swap(scoped_ptr & b); // never throws
};

template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws

}

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

#include <boost/scoped_ptr.hpp>
#include <iostream>

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
6
g++ -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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// scoped_ptr_example.hpp

// Boost scoped_ptr_example header file ------------------------------------//

// Copyright Beman Dawes 2001. 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)


// See http://www.boost.org/libs/smart_ptr for documentation.

#include <boost/utility.hpp>
#include <boost/scoped_ptr.hpp>

// The point of this example is to prove that even though
// example::implementation is an incomplete type in translation units using
// this header, scoped_ptr< implementation > is still valid because the type
// is complete where it counts - in the inplementation translation unit where
// destruction is actually instantiated.

class example : private boost::noncopyable
{
public:
example();
~example();
void do_something();
private:
class implementation;
boost::scoped_ptr< implementation > _imp; // hide implementation details
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// scoped_ptr_example.cpp

// Boost scoped_ptr_example implementation file -----------------------------//

// Copyright Beman Dawes 2001. 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)


// See http://www.boost.org/libs/smart_ptr for documentation.

#include "scoped_ptr_example.hpp"
#include <iostream>

class example::implementation
{
public:
~implementation() { std::cout << "destroying implementation\n"; }
};

example::example() : _imp( new implementation ) {}

void example::do_something() { std::cout << "did something\n"; }

example::~example() {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Boost scoped_ptr_example_test main program  -------------------------------//

// Copyright Beman Dawes 2001. 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)


// See http://www.boost.org/libs/smart_ptr for documentation.

#include "scoped_ptr_example.hpp"

int main()
{

example my_example;
my_example.do_something();
return 0;
}

编译运行

1
2
3
4
5
g++ -g scoped_ptr_example.cpp scoped_ptr_example_test.cpp -o scoped_ptr_example_test
./scoped_ptr_example_test

did something
destroying implementation