scoped_ptr
头文件: "boost/scoped_ptr.hpp"
boost::scoped_ptr 用于确保能够正确地删除动态分配的对象。scoped_ptr 有着与std::auto_ptr类似的特性,而最大的区别在于它不能转让所有权而auto_ptr可以。事实上,scoped_ptr永远不能被复制或被赋值!scoped_ptr 拥有它所指向的资源的所有权,并永远不会放弃这个所有权。scoped_ptr的这种特性提升了我们的代码的表现,我们可以根据需要选择最合适的智能指针(scoped_ptr 或 auto_ptr)。
要决定使用std::auto_ptr还是boost::scoped_ptr, 就要考虑转移所有权是不是你想要的智能指针的一个特性。如果不是,就用scoped_ptr. 它是一种轻量级的智能指针;使用它不会使你的程序变大或变慢。它只会让你的代码更安全,更好维护。
下面是scoped_ptr的摘要,以及对它的成员的简要描述:
namespace boost {
template<typename T> class scoped_ptr : noncopyable {
public:
explicit scoped_ptr(T* p = 0);
~scoped_ptr();
void reset(T* p = 0);
T& operator*() const;
T* operator->() const;
T* get() const;
void swap(scoped_ptr& b);
};
template<typename T>
void swap(scoped_ptr<T> & a, scoped_ptr<T> & b);
}
成员函数
explicit scoped_ptr(T* p=0)
构造函数,存储p的一份拷贝。注意,p 必须是用operator new分配的,或者是null. 在构造的时候,不要求T必须是一个完整的类型。当指针p是调用某个分配函数的结果而不是直接调用new得到的时候很有用:因为这个类型不必是完整的,只需要类型T的一个前向声明就可以了。这个构造函数不会抛出异常。
~scoped_ptr()
删除被指物。类型T在被销毁时必须是一个完整的类型。如果scoped_ptr在它被析构时并没有保存资源,它就什么都不做。这个析构函数不会抛出异常。
void reset(T* p=0);
重置一个 scoped_ptr 就是删除它已保存的指针,如果它有的话,并重新保存 p. 通常,资源的生存期管理应该完全由scoped_ptr自己处理,但是在极少数时候,资源需要在scoped_ptr的析构之前释放,或者scoped_ptr要处理它原有资源之外的另外一个资源。这时,就可以用reset,但一定要尽量少用它。(过多地使用它通常表示有设计方面的问题) 这个函数不会抛出异常。
T& operator*() const;
返回一个到被保存指针指向的对象的引用。由于不允许空的引用,所以解引用一个拥有空指针的scoped_ptr将导致未定义行为。如果不能肯定所含指针是否有效,就用函数get替代解引用。这个函数不会抛出异常。
T* operator->() const;
返回保存的指针。如果保存的指针为空,则调用这个函数会导致未定义行为。如果不能肯定指针是否空的,最好使用函数get。这个函数不会抛出异常。
T* get() const;
返回保存的指针。应该小心地使用get,因为它可以直接操作裸指针。但是,get使得你可以测试保存的指针是否为空。这个函数不会抛出异常。get通常用于调用那些需要裸指针的函数。
operator unspecified_bool_type() const
返回scoped_ptr是否为非空。返回值的类型是未指明的,但这个类型可被用于Boolean的上下文中。在if语句中最好使用这个类型转换函数,而不要用get去测试scoped_ptr的有效性
void swap(scoped_ptr& b)
交换两个scoped_ptr的内容。这个函数不会抛出异常。
普通函数
template<typename T> void swap(scoped_ptr<T>& a,scoped_ptr<T>& b)
这个函数提供了交换两个scoped pointer的内容的更好的方法。之所以说它更好,是因为 swap(scoped1,scoped2) 可以更广泛地用于很多指针类型,包括裸指针和第三方的智能指针。scoped1.swap(scoped2) 则只能用于它的定义所在的智能指针,而不能用于裸指针。
用法
scoped_ptr的用法与普通的指针没什么区别;最大的差别在于你不必再记得在指针上调用delete,还有复制是不允许的。典型的指针操作(operator* 和 operator->)都被重载了,并提供了和裸指针一样的语法。用scoped_ptr和用裸指针一样快,也没有大小上的增加,因此它们可以广泛使用。使用boost::scoped_ptr时,包含头文件"boost/scoped_ptr.hpp". 在声明一个scoped_ptr时,用被指物的类型来指定类模板的参数。例如,以下是一个包含std::string指针的scoped_ptr:
boost::scoped_ptr<std::string> p(new std::string("Hello"));
当scoped_ptr被销毁时,它对它所拥有的指针调用delete 。
不需要手工删除
让我们看一个程序,它使用scoped_ptr来管理std::string指针。注意这里没有对delete的调用,因为scoped_ptr是一个自动变量,它会在离开作用域时被销毁。
#include "boost/scoped_ptr.hpp"
#include <string>
#include <iostream>
int main() {
{
boost::scoped_ptr<std::string>
p(new std::string("Use scoped_ptr often."));
// 打印字符串的值
if (p)
std::cout << *p << '\n';
// 获取字符串的大小
size_t i=p->size();
// 给字符串赋新值
*p="Acts just like a pointer";
} // 这里p被销毁,并删除std::string
}
这段代码中有几个地方值得注明一下。首先,scoped_ptr可以测试其有效性,就象一个普通指针那样,因为它提供了隐式转换到一个可用于布尔表达式的类型的方法。其次,可以象使用裸指针那样调用被被指物的成员函数,因为重载了operator->. 第三,也可以和裸指针一样解引用scoped_ptr,这归功于operator*的重载。这些特性正是scoped_ptr和其它智能指针的用处所在,因为它们和裸指针的不同之处在于对生存期管理的语义上,而不在于语法上。
和auto_ptr几乎一样
scoped_ptr 与 auto_ptr间的区别主要在于对拥有权的处理。auto_ptr在复制时会从源auto_ptr自动交出拥有权,而scoped_ptr则不允许被复制。看看下面这段程序,它把scoped_ptr 和 auto_ptr放在一起,你可以清楚地看到它们有什么不同。
void scoped_vs_auto() {
using boost::scoped_ptr;
using std::auto_ptr;
scoped_ptr<std::string> p_scoped(new std::string("Hello"));
auto_ptr<std::string> p_auto(new std::string("Hello"));
p_scoped->size();
p_auto->size();
scoped_ptr<std::string> p_another_scoped=p_scoped;
auto_ptr<std::string> p_another_auto=p_auto;
p_another_auto->size();
(*p_auto).size();
}
这个例子不能通过编译,因为scoped_ptr不能被复制构造或被赋值。auto_ptr既可以复制构造也可以赋值,但这们同时也意味着它把所有权从p_auto 转移给了 p_another_auto, 在赋值后p_auto将只剩下一个空指针。这可能会导致令人不快的惊讶,例如你试图把auto_ptr放入容器内。 如果我们删掉对p_another_scoped的赋值,程序就可以编译了,但它的运行结果是不可预测的,因为它解引用了p_auto里的空指针(*p_auto).
由于scoped_ptr::get会返回一个裸指针,所以就有可能对scoped_ptr做一些有害的事情,其中有两件是你尤其要避免的。第一,不要删除这个裸指针。因为它会在scoped_ptr被销毁时再一次被删除。第二,不要把这个裸指针保存到另一个scoped_ptr (或其它任何的智能指针)里。因为这样也会两次删除这个指针,每个scoped_ptr一次。简单地说,尽量少用get, 除非你要使用那些要求你传送裸指针的遗留代码!
scoped_ptr 和Pimpl用法
scoped_ptr可以很好地用于许多以前使用裸指针或auto_ptr的地方,如在实现pimpl用法时。pimpl 用法背后的思想是把客户与所有关于类的私有部分的知识隔离开。由于客户是依赖于类的头文件的,头文件中的任何变化都会影响客户,即使仅是对私有节或保护节的修改。pimpl用法隐藏了这些细节,方法是将私有数据和函数放入一个单独的类中,并保存在一个实现文件中,然后在头文件中对这个类进行前向声明并保存一个指向该实现类的指针。类的构造函数分配这个pimpl类,而析构函数则释放它。这样可以消除头文件与实现细节的相关性。我们来构造一个实现pimpl 用法的类,然后用智能指针让它更安全些。
// pimpl_sample.hpp
#if !defined (PIMPL_SAMPLE)
#define PIMPL_SAMPLE
class pimpl_sample {
struct impl; // 译者注:原文中这句在class之外,应有误
impl* pimpl_;
public:
pimpl_sample();
~pimpl_sample();
void do_something();
};
#endif
这是类pimpl_sample的接口。struct impl 是一个前向声明,它把所有私有成员和函数放在另一个实现文件中。这样做的效果是使客户与pimpl_sample类的内部细节完全隔离开来。
// pimpl_sample.cpp
#include "pimpl_sample.hpp"
#include <string>
#include <iostream>
struct pimpl_sample::impl {
void do_something_() {
std::cout << s_ << "\n";
}
std::string s_;
};
pimpl_sample::pimpl_sample()
: pimpl_(new impl) {
pimpl_->s_ = "This is the pimpl idiom";
}
pimpl_sample::~pimpl_sample() {
delete pimpl_;
}
void pimpl_sample::do_something() {
pimpl_->do_something_();
}
看起来很完美,但并不是的。这个实现不是异常安全的!原因是pimpl_sample的构造函数有可能在pimpl被构造后抛出一个异常。在构造函数中抛出异常意味着已构造的对象并不存在,因此在栈展开时将不会调用它的析构函数。这样就意味着分配给impl_指针的内存将泄漏。然而,有一样简单的解决方法:用scoped_ptr来解救!
class pimpl_sample {
struct impl;
boost::scoped_ptr<impl> pimpl_;
...
};
让scoped_ptr来处理隐藏类impl的生存期管理,并从析构函数中去掉对impl的删除(它不再需要,这要感谢scoped_ptr),这样就做完了。但是,你必须记住要手工定义析构函数;原因是在编译器生成隐式析构函数时,类impl还是不完整的,所以它的析构函数不能被调用。如果你用auto_ptr来保存impl, 你可以编译,但也还是有这个问题,但如果用scoped_ptr, 你将收到一个错误提示。
要注意的是,如果你使用scoped_ptr作为一个类的成员,你就必须手工定义这个类的复制构造函数和赋值操作符。原因是scoped_ptr是不能复制的,因此聚集了它的类也变得不能复制了。
最后一点值得注意的是,如果pimpl实例可以安全地被多个封装类(在这里是pimpl_sample)的实例所共享,那么用boost::shared_ptr来管理pimpl的生存期才是正确的选择。用shared_ptr比用scoped_ptr的优势在于,不需要手工去定义复制构造函数和赋值操作符,而且可以定义空的析构函数,shared_ptr被设计为可以正确地用于未完成的类。
scoped_ptr 不同于 const auto_ptr
留心的读者可能已经注意到auto_ptr可以几乎象scoped_ptr一样地工作,只要把auto_ptr声明为const:
const auto_ptr<A> no_transfer_of_ownership(new A);
它们很接近,但不是一样。最大的区别在于scoped_ptr可以被reset, 在需要时可以删除并替换被指物。而对于const auto_ptr这是不可能的。另一个小一点的区别是,它们的名字不同:尽管const auto_ptr意思上和scoped_ptr一样,但它更冗长,也更不明显。当你的词典里有了scoped_ptr,你就应该使用它,因为它可以更清楚地表明你的意图。如果你想说一个资源是要被限制在作用域里的,并且不应该有办法可以放弃它的所有权,你就应该用 boost::scoped_ptr.
总结
使用裸指针来写异常安全和无错误的代码是很复杂的。使用智能指针来自动地把动态分配对象的生存期限制在一个明确的范围之内,是解决这种问题的一个有效的方法,并且提高了代码的可读性、可维护性和质量。scoped_ptr 明确地表示被指物不能被共享和转移。正如你所看到的,std::auto_ptr可以从另一个auto_ptr那里窃取被指物,那怕是无意的,这被认为是auto_ptr的最大的缺点。正是这个缺点使得scoped_ptr成为auto_ptr最好的补充。当一个动态分配的对象被传送给scoped_ptr, 它就成为了这个对象的唯一的拥有者。因为scoped_ptr几乎总是以自动变量或数据成员来分配的,因此它可以在离开作用域时正确地销毁,从而在执行流由于返回语句或异常抛出而离开作用域时,总能释放它所管理的内存。
在以下情况时使用 scoped_ptr :
-
在可能有异常抛出的作用域里使用指针
-
函数里有几条控制路径
-
动态分配对象的生存期应被限制于特定的作用域内
-
异常安全非常重要时(始终如此!)
|
相关推荐
Boost库提供了一组智能指针类型,包括scoped_ptr、shared_ptr和weak_ptr,它们各自有特定的用途和限制。 ### 1. scoped_ptr `boost::scoped_ptr` 是一种非拷贝性的智能指针,它的设计目标是保证对象在其作用域内...
在C++11及更高版本中,智能指针(如`std::unique_ptr`,`std::shared_ptr`和`std::scoped_ptr`)被引入来更好地管理动态内存,因此理解如何在这些指针上实现序列化至关重要。 首先,让我们关注`std::unique_ptr`。`...
Boost库提供了一种名为`boost::scoped_ptr`的智能指针,它是一种简单的智能指针类型,适用于管理单个非托管对象。与`std::auto_ptr`相似,`boost::scoped_ptr`在对象生命周期结束时会自动调用`delete`释放内存。 `...
例如,如果你需要确保对象只被单个智能指针管理,并且该智能指针在超出作用域时自动销毁对象,那么`scoped_ptr`是一个合适的选择。另一方面,如果你需要允许多个智能指针共享对同一对象的所有权,那么应该使用`...
Boost智能指针是由C++标准委员会库工作组支持的Boost组织开发的一套智能指针解决方案,主要包括五种智能指针模板类:`scoped_ptr`、`scoped_array`、`shared_ptr`、`shared_array`以及`weak_ptr`。 ##### 1. `...
#### 三、第二类智能指针:`scoped_ptr`、`scoped_array` 和 `scoped_ptr_malloc` 第二类智能指针要求对象在同一时间只能被一个智能指针所拥有。一旦智能指针超出其作用域,它所管理的对象就会被自动释放。这类智能...
1. **scoped_ptr**:负责管理单个对象的唯一所有权,类似于标准库中的`std::auto_ptr`,但不支持赋值操作,从而避免了所有权转移的问题。`scoped_ptr`的引入使得资源管理更加安全可靠。 - 特点: - 不可复制:一旦...
本文将详细介绍 STL 中的智能指针,包括 std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost::intrusive_ptr 等七种智能指针。 一、简介 ...
●拿到脚本后,可以利用pyinstaller库进行封装生成一个程序。方便点击一键删除缓存。 ●代码:pyinstaller -F 文件名.py,生成程序后,在py文件目录下生成的dist文件夹里生成程序文件。直接将其拷贝到自己能操作的...
5. **boost::scoped_ptr**和**boost::scoped_array**:这两个是Boost库中的智能指针,类似于`std::unique_ptr`,但不支持移动语义和类型转换,适用于简单的所有权转移场景。 6. **boost::intrusive_ptr**:这个智能...
Boost中的`smart_ptr`库提供了多种智能指针类型,如`scoped_ptr`、`shared_ptr`、`weak_ptr`等,每种类型都有其适用场景。 - **scoped_ptr**:提供了一种简单的方法来拥有一个单一对象,并确保该对象在其作用域结束...
在内存管理方面,Boost库提供了智能指针如`scoped_ptr`和`scoped_array`,它们确保了对象在作用域结束时会被正确释放,防止内存泄漏。`scoped_ptr`类似于`auto_ptr`,但不支持所有权转让,保证了对象仅在当前作用域...
- **Scoped_ptr**: 是最简单的智能指针类型,仅负责管理单个对象,当scoped_ptr对象销毁时,它所管理的对象也会被自动释放。 - **Scoped_array**: 与scoped_ptr类似,但用于管理数组。 - **Shared_ptr**: 支持共享...
《Beyond the C++ Standard Library: An Introduction to Boost》这本书由Björn Karlsson编写,详细介绍了Boost库的核心组成部分及其使用方法,尤其适合中高级C++开发者阅读。 #### 二、智能指针(Library 1:...
1. scoped_ptr:类似auto_ptr,但其一旦获得对象的管理权,你就无法再从它那里取回来。 2. scoped_array:包装的是new[]产生的指针,并调用的是delete[]。 3. shared_ptr:支持的转型有:static_pointer_cast、const...
8. **智能指针和资源管理**:深入讨论智能指针的使用,以及自定义资源管理策略,如scoped_ptr、intrusive_ptr等。 9. **并发和多线程**:包括线程库(thread)、互斥量(mutex)、条件变量(condition_variable)等...