c++详解【智能指针】

智能指针?是一个指针吗?这里给大家说的是,它不是一个指针,但它模拟了指针所具有的功能。那么,为什么要有智能指针的引入呢?看看下面的例子吧~

void FunTest()
{int *p = new int[10];FILE *pFile = fopen("1.txt","r");if(pFile == NULL){return;}if(p){delete[] p;p = NULL;}
}
在c++动态内存分配空间时,是由用户自己维护的,所以当new出来空间后,必须由用户手动释放。再看看上面的这个代码,有什么问题呢?显然的,当1.txt这个文件不存在时,pFile就为空,这就使后面释放p的语句没有执行,导致内存泄漏。那么怎么解决这类问题呢?

这就引入了我们重要的智能指针,所谓智能指针就是智能化的管理动态开辟的资源的释放工作。

1、模拟实现auto_ptr

template<typename T>
class AutoPtr
{
public:AutoPtr(T* p):_p(new T(1)){cout<<"AutoPtr()"<<endl;_p = p;}AutoPtr(AutoPtr& ap):_p(ap._p){cout<<"AutoPtr(AutoPtr& ap)"<<endl;ap._p = NULL;}AutoPtr<T>& operator=(AutoPtr& ap){cout<<"AutoPtr<T>& operator=(AutoPtr& ap)"<<endl;if(this != &ap){delete _p;_a = ap._p;ap._p = NULL;}}~AutoPtr(){cout<<"~AutoPtr()"<<endl;if (_p){delete _p;_p = NULL;}}
public:T& operator*(){return *_p;}T* operator->(){return _p;}
private:T *_p;
};void FunTest()
{AutoPtr<int> ap = new int;AutoPtr<int> ap1(ap);*ap1 =  10;*(ap1.operator->()) = 20;
}
int main()
{FunTest();return 0;
}

智能指针AutoPtr的特点是:只可以管理一个对象,看一下监视窗口:



这样就会导致资源被转移。

当然,AutoPtr还有另一种实现方式:

template<typename T>
class AutoPtr
{
public:AutoPtr(T* p):_p(new T(1)){cout<<"AutoPtr()"<<endl;_p = p;_owner = true;}AutoPtr(AutoPtr& ap):_p(ap._p){cout<<"AutoPtr(AutoPtr& ap)"<<endl;ap._owner = false;ap._p = NULL;_owner = true;}AutoPtr<T>& operator=(AutoPtr& ap){cout<<"AutoPtr<T>& operator=(AutoPtr& ap)"<<endl;if(this != &ap){delete _p;_a = ap._p;ap._owner = false;ap._p = NULL;_owner = true;}}~AutoPtr(){cout<<"~AutoPtr()"<<endl;if (_p){delete _p;_p = NULL;_owner = false;}}
public:T& operator*(){return *_p;}T* operator->(){return _p;}
private:T *_p;bool _owner;
};void FunTest()
{AutoPtr<int> ap = new int;AutoPtr<int> ap1(ap);*ap1 =  10;*(ap1.operator->()) = 20;}int main()
{FunTest();return 0;
}

这种方式就是定义一个私有成员,用来标记当前对象的状态,若为false,表示当前对象已不再指向任何空间,若为true,说明该对象指向申请的那块内存。



那么,这种类型的指针有什么弊端吗?首先,只能管理单个对象,每次只有一个对象可使用申请的空间。其次,使得资源转移,另一个对象使用时,前一个对象将赋为空。

但是切记不要使用auto_ptr

2、模拟实现scoped_ptr

scoped_ptrauto_ptr一样,都是管理单个对象的。它的作用是:在一个类中防止拷贝。说起防止拷贝,大家会想到把它定义为私有的,总可以了吧。其实并不可以,因为访问私有成员或函数的方式还有将调用它的函数声明为类的友元就ok啦。所以,在这里,我们将学到一种防拷贝的方式:只声明不定义,且将拷贝构造和赋值运算符重载声明为私有即可。

实现如下:

template<typename T>
class ScopedPtr
{
public:ScopedPtr(const T* p):_p(p){}~ScopedPtr(){if(_p){delete _p;_p = NULL;}}T& operator*(){return *_p;}T* operator->(){return _p;}
private:ScopedPtr(const ScopedPtr&);ScopedPtr<T>& operator=(const ScopedPtr&);
private:T* _p;
};void FunTest()
{ScopedPtr<int> sp = new int;ScopedPtr<int> sp1(sp);    //error,这样就使用不了拷贝构造函数了ScopedPtr<int> sp1 = sp;   //error,使用不了赋值运算符重载}int main()
{FunTest();return 0;
}

scoped_ptr实现的机制体现了它的独占性,当一个对象占用一块空间时,其他对象将无法使用。并且解决了不让资源转移的问题。

注:在STL源码库中使用的是scoped_ptr,而在boost库中,使用的是unique_ptr。两个其实是一样的。

3、模拟实现shared_ptr

前面两个智能指针都是管理单个对象,而这个shared_ptr则是可以管理多个对象。它们之间的资源是共享的。

以下代码是使用引用计数的方式实现资源共享。

实现代码如下:

template<typename T>
class SharedPtr
{
public:SharedPtr(T* p = NULL):_p(p),_pCount(new int(1)){cout<<"SharedPtr(T* p = NULL)"<<endl;}SharedPtr(SharedPtr& sp):_p(sp._p),_pCount(sp._pCount){cout<<"SharedPtr(SharedPtr& sp)"<<endl;}SharedPtr<T>& operator=(const SharedPtr& sp){cout<<"SharedPtr<T>& operator=(const SharedPtr& sp)"<<endl;if(_p != sp._p){if(_p == NULL){_p = sp._p;_pCount = sp._pCount;}else if(_p && (*_pCount== 1)){delete _p;_p = sp._p;_pCount = sp._p;}else{--(*_pCount);_p = sp._p;_pCount = sp._pCount;}if(sp._p)++(*_pCount);}return *this;}~SharedPtr(){cout<<"~SharedPtr()"<<endl;if(_p && --(*_pCount) == 0){delete _p;_p = NULL;}}
public:T& operator*(){return *_p;}T* operator->(){return _p;}int UseCount(){if(_pCount != NULL)return *_pCount;return 0;}private:T *_p;int *_pCount;
};void FunTest()
{SharedPtr<int> sp = new int(1);SharedPtr<int> sp1(sp);SharedPtr<int> sp2;sp = sp1;sp2 = sp1;cout<<sp.UseCount()<<endl;cout<<sp1.UseCount()<<endl;cout<<sp2.UseCount()<<endl;
}int main()
{FunTest();return 0;
}

运行结果:



此版本虽然实现了共享,但也存在着不少问题,比如线程安全问题、循环引用问题,定置删除器问题等。这几个问题我们下一篇再详细解说哦。


希望大家可以对博客提出宝贵意见,欢迎来访哦。微笑微笑

















本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382803.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++【深度剖析shared_ptr】

shared_ptr解决了scoped_ptr管理单个对象的缺陷&#xff0c;且解决了防拷贝的问题。shared_ptr可以管理多个对象&#xff0c;并且实现了资源共享。 但是仍然存在一些问题&#xff0c;比如&#xff0c;我们熟悉的双向链表&#xff1a; struct Node { Node(const int& value…

c++详解【new和delete】

说起new和delete&#xff0c;了解过c的人应该都知道吧&#xff0c;它是用来分配内存和释放内存的两个操作符。与c语言中的malloc和free类似。 c语言中使用malloc/calloc/realloc/free进行动态内存分配&#xff0c;malloc/calloc/realloc用来在堆上分配空间&#xff0c;free将申…

Stack/Queue与Vector/List的联系

Vector:(顺序表【数组存储】) 1.当申请的空间不足的时候&#xff0c;需要再次开辟一块更大的空间&#xff0c;并把值拷过去。 2.对于尾删和尾插是比较方便的&#xff0c;只需要改动最后一个元素即可。不会改动原有的空间。适用于多次重复的对尾部插删。 3.顺序存储&#xff…

用栈实现后缀表达式求解问题

一、问题概述&#xff1a; 人们经常书写的数学表达式属于中缀表达式&#xff0c;今天要解决的是&#xff0c;后缀表达式的求解问题。 如下图分别为举例的中缀表达式和后缀表达式&#xff1a; 二、解决思路 我们用栈存储后缀表达式中的数据部分&#xff0c;当遇到操作符时就取…

用栈和递归求解迷宫问题

一、问题概述 小时候&#xff0c;我们都玩过走迷宫的游戏吧。看一下这个图例&#xff1a; 遇到这种问题时&#xff0c;我们第一反应都会先找到迷宫的入口点&#xff0c;然后对上下左右四个方向进行寻迹&#xff0c; 检测当前位置是否是通路&#xff0c;是否可以通过&#xff0…

【c语言】棋盘游戏--三子棋

一、问题概述 大家都玩过棋盘游戏吧&#xff0c;像五子棋一样&#xff0c;玩家或者是电脑一人下一次&#xff0c;当玩家或者是电脑的某一方先将各自的五个棋子下成一条线时&#xff0c;谁就赢&#xff0c;棋盘游戏就会结束。 当然&#xff0c;我今天要介绍的是三子棋&#xff…

[数据结构]求解迷宫最短路径问题

一、问题概述 之前&#xff0c;我们了解了如何实现迷宫问题&#xff08;对于迷宫只有一个出口可以通的情况&#xff09;&#xff0c;事实上我们的迷宫有多个出口&#xff0c;对于每条路径来说&#xff0c;有长有短&#xff0c;所以在这里&#xff0c;我们讨论一下迷宫的最短路…

[STL]List的实现

STL&#xff08;Standard template Library&#xff09;:c的标准模板库 STL是算法和数据结构的软件框架&#xff0c;它包含了六大组件&#xff1a;算法、迭代器、容器、仿函数、配接器、空间配置器。 迭代器&#xff1a;我们可以把迭代器相当于智能指针&#xff0c;&#xff0…

vc++6.0的应用程序打不开肿么办

今天早起&#xff0c;有同学问到我关于vc6.0的安装过程中遇到的问题&#xff0c;我听了之后想想还是写篇博客给大家看一下吧。因为我之前也遇到过类似的问题。当时也是挺着急的。 大家遇到的问题估计就是这样吧~~&#xff08;下载后打不开&#xff09; 请看-->解决步骤&…

【数据结构】广义表

一、问题概述 广义表是非线性的数据结构&#xff0c;是由若干个元素组合而成的&#xff0c;广义表中可以有子表&#xff0c;类似这样的&#xff1a; 我们以C(a,b,(c,d))为例&#xff0c;将它定义为这样的数据结构&#xff1a; 我们会给定字符串的形式&#xff0c;如&#xff…

【数据结构】普通二叉树的实现

一、问题概述 树是n个有限个数据的集合&#xff0c;形如&#xff1a; 它像不像倒着的树呢&#xff1f;我们把它看成是一种数据结构----树。它的第一个节点称作树的根&#xff0c;最底下的那些节点称作树的叶子。 我们今天所要研究的是二叉树&#xff0c;即父节点最多只有两个孩…

setitimer用法说明

函数原型&#xff1a; int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value) 函数作用&#xff1a; 可用来实现延时和定时的功能 头文件&#xff1a; #include <sys/time.h> 参数详解 用一把&#xff1a;一个例子 #include &…

哈希表(闭散列、拉链法--哈希桶)

哈希表&#xff0c;也称散列表&#xff0c;是一种通过key值来直接访问在内存中的存储的数据结构。它通过一个关键值的函数&#xff08;被称为散列函数&#xff09;将所需的数据映射到表中的位置来访问数据。 关于哈希表&#xff0c;主要为以下几个方面&#xff1a; 一、哈希表…

僵尸进程的产生和SIGCHLD信号

核心句子 子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数。 僵尸进程的产生&#xff1a; #include "head.h" #include <unistd.h> #include <signal.h>int main() {key_t key ftok(&quo…

海量数据处理--位图(BitMap)

对于海量数据这个词&#xff0c;大家不难理解吧。主要是针对给定的数据量特别大&#xff0c;占用内存特别大的情况。那么和位图有什么关系呢。看下面一个腾讯的海量数据的例子吧。 例&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0…

ps命令与top命令参数意义详解

文章目录1.ps -l2.ps aux3.top面试经常被问道&#xff0c;特别是top。1.ps -l 参数解释F代表这个程序旗标 (process flags)&#xff0c;说明这个程序的总结权限&#xff0c;常见号码有&#xff1a;o 若为 4 表示此程序的权限为 root &#xff1b;o 若为 1 则表示此子程序仅进行…

哈希拓展--布隆过滤器

一、问题概述 布隆过滤器是由布隆提出来的&#xff0c;是由一个很长的二进制序列和一系列的映射函数组成。主要用于检测一个元素是否在一个集合中。当然在设计计算机软件时&#xff0c;我们也经常会判断一个元素是否在一个集合中。比如&#xff1a;在字处理软件中&#xff0c;…

排序(Sort)--【一】

排序&#xff0c;对于大家再熟悉不过了吧。我们之前在学习c语言的时候接触过的冒泡排序&#xff0c;选择排序等。今天给大家介绍两种新的排序。 1、直接插入排序 升序排列&#xff1a;将第一个数确定好&#xff0c;从下标为1的数开始插入&#xff0c;如果插入的数比前一个数大…

快速排序--全集

快速排序&#xff1a;一听名字就知道这种排序很快的&#xff0c;是吧&#xff1f;没错&#xff0c;它是一种效率比较高的排序算法。 快速排序采用的是分治的思想。 比如&#xff0c;将一串数中的一个元素作为基准&#xff0c;然后将比它小的数排在它的左边&#xff0c;比它大…

task_struct结构体查找

网上有很多解析task_struct结构体的文章&#xff0c;可是都没有说这个结构体到底在哪里&#xff1f; 这个结构体位于头文件 shced.h cd / find -name sched.h 显示结果如下 注意只有 位于内核中的include 才是正确的。 /usr/src/kernels/2.6.32-431.el6.i686/include/linux…