(给CPP开发者加星标,提升C/C++技能)
来源:造轮子https://blog.youkuaiyun.com/Marble_ccp/article/details/105606236
前言
学习过C/C++的同学都知道,有一个非常方便又特别让人烦的数据类型,那就是指针,不仅操作麻烦,还有各种“级别”,一级指针、二级指针、n级别指针…很多人从入门到放弃C/C++,很大一个原因是因为搞不清楚指针、以及指针与其他对象之间千丝万缕的关系。而在实际的开发过程中,经常会出现因为未释放申请的对象而导致内存溢出、程序奔溃。这里就包括指针对象的释放,那如果有一种指针,能申请对象后“自动”释放,是不是很爽?本文将介绍Boost中提到的各种智能指针。
C++中出现智能是在1998年修订的第一版C++标准中:std::auto_ptr 。与下面boost中的其他智能指针相比,它就像是个普通的指针:通过地址来访问一个动态分配的对象。它会在析构时调用 delete 操作符来自动释放所包含的对象。前提是在初始化的时候,传给它一个由 new 操作符返回的对象地址。这就明显减少了程序员手动delete的繁琐操作。
另一方便,程序中频繁出现的“异常”,也可以导致未正确释放内存,而出现内存泄漏。而智能指针,可以在析构函数中准确释放内存,避免出现内存泄漏。这也是智能指针的优点之一。Boost C++ 库 Smart Pointers 提供了许多可以用在各种场合的智能指针。
你知道RAII吗?
习语 RAII(Resource Acquisition Is Initialization) :资源申请即初始化。智能指针是该习语重要的实现之一。智能指针确保在任何情况下,动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放出来。这包括程序因为异常而中断,原本用于释放内存的代码被跳过的场景。用一个动态分配的对象的地址来初始化智能指针,在析构的时候释放内存,就确保了这一点。因为析构函数总是会被执行的,这样所包含的内存也将总是会被释放。
无论何时,一定得有第二条指令来释放之前另一条指令所分配的资源时,RAII 都是适用的。许多的 C++ 应用程序都需要动态管理内存,因而智能指针是一种很重要的 RAII 类型。不过 RAII 本身是适用于许多其它场景的。
基于上述描述,如下代码片段,模拟智能指针实现。
// Smart_ptr.hpp#include 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 #include 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"int main(){ Smart_ptr p = Smart_ptr(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 p(new Person("小红", 12)); p->Show(); // 等价于 p.get()->Show(); //boost::scoped_ptr p1(p);; // 无法使用scoped_ptr拷贝构造定义初始化另一个对象 return 0;}
执行结果
一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。这是因为重载了相关的操作符,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 #include #include #include #include #include #include #ifndef BOOST_NO_AUTO_PTR# include // for std::auto_ptr#endifnamespace 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 s