Boost中的智能指针详细总结

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 != 
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值