文章目录
1、前言
学习过C/C++的同学都知道,有一个非常方便又特别让人烦的数据类型,那就是指针,不仅操作麻烦,还有各种“级别”,一级指针、二级指针、n级别指针…很多人从入门到放弃C/C++,很大一个原因是因为搞不清楚指针、以及指针与其他对象之间千丝万缕的关系。而在实际的开发过程中,经常会出现因为未释放申请的对象而导致内存溢出、程序奔溃。这里就包括指针对象的释放,那如果有一种指针,能申请对象后“自动”释放,是不是很爽?本文将介绍Boost中提到的各种智能指针。
C++中出现智能是在1998年修订的第一版C++标准中: std::auto_ptr 。 与下面boost中的其他智能指针相比,它就像是个普通的指针: 通过地址来访问一个动态分配的对象。 它会在析构时调用 delete 操作符来自动释放所包含的对象。 前提是在初始化的时候,传给它一个由 new 操作符返回的对象地址。这就明显减少了程序员手动delete的繁琐操作。
另一方便,程序中频繁出现的“异常”,也可以导致未正确释放内存,而出现内存泄漏。而智能指针,可以在析构函数中准确释放内存,避免出现内存泄漏。这也是智能指针的优点之一。 Boost C++ 库 Smart Pointers 提供了许多可以用在各种场合的智能指针。
2、你知道RAII吗?
习语 RAII(Resource Acquisition Is Initialization) :资源申请即初始化。智能指针是该习语重要的实现之一。 智能指针确保在任何情况下,动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放出来。 这包括程序因为异常而中断,原本用于释放内存的代码被跳过的场景。 用一个动态分配的对象的地址来初始化智能指针,在析构的时候释放内存,就确保了这一点。 因为析构函数总是会被执行的,这样所包含的内存也将总是会被释放。
无论何时,一定得有第二条指令来释放之前另一条指令所分配的资源时,RAII 都是适用的。 许多的 C++ 应用程序都需要动态管理内存,因而智能指针是一种很重要的 RAII 类型。 不过 RAII 本身是适用于许多其它场景的。
基于上述描述,如下代码片段,模拟智能指针实现。
// Smart_ptr.hpp
#include <iostream>
using namespace std;
template<class T>
class Smart_ptr
{
public:
Smart_ptr(T *t)
{
cout << "Smart_ptr 构造函数" << endl;
this->t = t;
}
~Smart_ptr()
{
if (this->t != NULL)
{
cout << "Smart_ptr 西沟函数" << endl;
delete this->t;
}
}
T* &operator->()
{
return this->t;
}
private:
T *t;
};
构造Person测试类
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(std::string name, int age) :name(name), age(age)
{
cout << "Person 的构造函数" << endl;
}
Person()
{
cout << "Person 的默认构造" << endl;
}
void operator=(const Person& p)
{
this->age = p.age;
this->name = p.name;
}
~Person()
{
cout << "Person 的析构函数" << endl;
}
void Show()
{
cout << this->name << "小朋友今年" << this->age << "岁了" << endl;
}
private:
std::string name;
int age;
};
测试代码
#include"Smart_ptr.hpp"
#include"Person.hpp"
nt main()
{
Smart_ptr<Person> p = Smart_ptr<Person>(new Person("小花", 22));
p->Show();
return 0;
}
运行结果

从测试代码中看出,我new了Person对象,在程序结束时并没有delete,但是却打印出了调用析构函数的日志【不信你可以断点调试这个程序,O(∩_∩)O哈哈~】。这就说明智能指针已经起作用了,帮你省掉了delete的步骤。是不是很开心^-^。
上述只是简单地引入了一下智能指针的概念,以及描述了智能指针的作用,下面将着重介绍boost库中提供的强大的智能指针。
3、作用域指针
3.1、概述
作用域指针类名为: boost::scoped_ptr。 一个作用域指针独占一个动态分配的对象。它的定义在 boost/scoped_ptr.hpp 中。 不像 std::auto_ptr,一个作用域指针不能传递它所包含的对象到另一个作用域指针,也就是说无法通过拷贝构造传参。 一旦用一个地址来初始化,这个动态分配的对象将在析构阶段释放。
一个作用域指针只独占一个内存地址,所以 boost::scoped_ptr 的实现就要比 std::auto_ptr 简单。 在不需要拷贝构造传递的时候应该优先使用 boost::scoped_ptr 。
3.2、如何使用scoped_ptr
下面代码片段,演示如何调用boost::scoped_ptr。其中Person对象贯穿全篇,定义在本文开头处。
int main()
{
boost::scoped_ptr<Person> p(new Person("小红", 12));
p->Show(); // 等价于 p.get()->Show();
//boost::scoped_ptr<Person> p1(p);; // 无法使用scoped_ptr拷贝构造定义初始化另一个对象
return 0;
}
执行结果

一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。 这是因为重载了相关的操作符 operator()*,operator->() 和 operator bool() 。 此外,还有 get() 和 reset() 方法。 前者返回所含对象的地址,后者用一个新的对象来重新初始化智能指针。 在这种情况下,新创建的对象赋值之前会先自动释放所包含的对象。
boost::scoped_ptr 的析构函数中使用 delete 操作符来释放所包含的对象。 这对 boost::scoped_ptr 所包含的类型加上了一条重要的限制。 即boost::scoped_ptr 不能用动态分配的数组来做初始化,因为这需要调用 delete[] 来释放。 在这种情况下,可以使用下面将要介绍的 boost:scoped_array 类。
3.3、scoped_ptr源码分析
如下代码片段是截取的boost::scoped_ptr源码核心部分,相关解释见注释。
// scoped_ptr.hpp
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/checked_delete.hpp>
#include <boost/smart_ptr/detail/sp_nullptr_t.hpp>
#include <boost/smart_ptr/detail/sp_disable_deprecated.hpp>
#include <boost/smart_ptr/detail/sp_noexcept.hpp>
#include <boost/detail/workaround.hpp>
#ifndef BOOST_NO_AUTO_PTR
# include <memory> // for std::auto_ptr
#endif
namespace boost
{
// 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;
// 逻辑运算符==和!=同样私有化,不允许比较两个指针对象。
void operator==( scoped_ptr const& ) const;
void operator!=( scoped_ptr const& ) const;
public:
typedef T element_type;
// 构造函数,不能通过隐式类型转换构造对象,只能通过显示类型构造对象。
explicit scoped_ptr( T * p = 0 ) BOOST_SP_NOEXCEPT : px( p )
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_constructor_hook( px );
#endif
}
#ifndef BOOST_NO_AUTO_PTR
explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_SP_NOEXCEPT : px( p.release() )
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_constructor_hook( px );
#endif
}
#endif
// scoped_ptr 析构函数,可以看出调用了delete释放对象内存
~scoped_ptr() BOOST_SP_NOEXCEPT
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_destructor_hook( px );
#endif
boost::checked_delete( px );
}
// 重置对象为默认值,类似p=NULL
void reset(T * p = 0) BOOST_SP_NOEXCEPT_WITH_ASSERT
{
BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
this_type(p).swap(*this);
}
T & operator*() const BOOST_SP_NOEXCEPT_WITH_ASSERT
{
BOOST_ASSERT( px !=

最低0.47元/天 解锁文章
540





