C++中野指针问题分析总结
2014-09-20 23:33
876 查看
其实项目一直被野指针困扰,一直在讨论,居然没有什么下文,下文正式由我接受这个问题。
场景是这样:指针间互相引用,一个被删除的时候,另外一个不知道,它再使用的时候就会异常,因为此时指针已经为空了。
我今天提出的解决方案是:
所以实体都在一个地方管理,其他地方都保存其标识号,每次用的时候根据其标识取就行了,这样看起来最简单,保存的地方我们用hash_map,每次操作O(1)复杂度。
现在觉得还造不成hotpot。
不过刚才在网上,早就有网友遇到类似问题,并做了总结。
知然博友,在“安全编程-c++野指针和内存泄漏”提出的思路跟我的类似:
现在全文引用,备查:
尽管C++ 野指针和内存泄漏一直被诟病,但是在实时性很强的应用场合,c++ 仍然是不二之选。游戏服务器开发仍然使用c++ 作为主语言,但是大多结合动态脚本技术,一方面规避了野指针和内存泄露,一方面获得了开发效率和扩展性的红利。但脚本技术不是本文的讨论重点,事实上关于c++ 与 lua的技术文章我也一直在整理中,将会另文别述。今天主要说说在使用c++过程中,如何避免和解决野指针和内存泄漏问题。
野指针的出现会导致程序崩溃,这是每个人都不愿意看到的。Linux会生成coredump文件,可用gdb分析。Win下可以注册unexception获取调用堆栈,将错误信息写到文件中。先分析一下通常出现野指针的场景:
问题就在于,m_attack有值,但是对应的对象已经被销毁了。这是大部分野指针出现原因。分析类之间关系可知,monster_t 和 player_t是0-1的关系,monster_t引用player_t,但是player_t甚至都不知道有一个(或N个)monster 引用了自己。所以当player被销毁时,很难做到把所有引用该player_t的地方全部重置。这种问题其实比较常见,比如player中引用connection,而connection又是被网络层管理生命周期的,也同样容易产生野指针情况。常见的解决方式是:
另外一种与之相似的方式:
梳理野指针的产生原因后,我们其实需要的是这样的指针:
一种指针,引用了另一个对象的地址(不然就不是指针了),当目标对象销毁时,该指针自然指向null,而不需要目标对象主动通知重置。
幸运的是,这种指针已经有了,就是weak_ptr; 在boost库中,sharedptr,scopedptr,weakptr统称为smartptr。可以尽量使用智能指针,避免野指针。本人建议尽量使用shared_ptr结合weak_ptr使用。Scoped_ptr本人使用的较少,只是在创建线程对象的时候使用,正好符合不能复制的语义。使用shared_ptr和weak_ptr的示例代码:
有人问monster_t为什么不直接使用shared_ptr,如果使用shared_ptr就不符合现实的模型了,monster_t显然不应该控制player_t的生命周期,如果使用了shared_ptr,那么可能导致player_t被延迟析构,甚至会导致内存暴涨。这也是shared_ptr的使用误区,所以本人建议尽量shared_ptr和weak_ptr结合用,否则野指针问题解决了,内存泄漏问题又来了。
野指针问题可以通过采用良好的编程范式,尽量规避,但总计c++规避内存泄漏的方法却很为难,简单而言尽量保证对象的分配和释放(分别)是单个入口的,这样大部分问题都可以拦截在code review阶段。那么怎么检测内存泄漏呢?
首先说明本方法区别于valgrind等工具,该工具是调试期进行的检测,本文探究的是运行期的检测,确切说是运行期定时输出所有对象的数量到日志中。
首先定义分配、释放对象的接口:
为了节省篇幅,这里只列举了三种构造的代码,当分配对象时,对应的类型数量增加1,obj_counter 使用原子操作为每一种类型记录其数量。
相应的当对象被释放的时候,对应的对象数量减一,示例代码如下:
这样就做到了所有的对象的数量都被记录了,可以定时的将对象数量输出到文件:
输出的文件格式为csv格式,方便进一步做数据分析。可以使用我开发的小工具格式化csv数据。url:http://ffown.sinaapp.com/perf/csv.html
文件内容data:
obj,num,20120606-17:01:41
dumy,1111
foo,222
obj,num,20120606-18:01:41
dumy,11311
foo,2422
obj,num,20120606-19:01:41
dumy,41111
foo,24442
总结:
野指针可以使用shared_ptr和weak_ptr结合使用来尽量规避。
使用shared_ptr要尽量小心,否则可能导致对象无法释放,导致内存泄漏。
可以定时输出当前所有对象的数量,来分析是否有内存泄漏,或者内存泄漏是有哪些对象引起的。
本文介绍了记录所有对象的方法,除了可以分析内存泄漏外,也不失为数据分析的一种方法。需要注明的是,本方法不能替代valgrind工具,二者作用不同。
TYPE_NAME 的实现参考
https://ffown.googlecode.com/svn/trunk/fflib/include/type_i.h
全部示例代码:https://ffown.googlecode.com/svn/trunk/fflib/include/obj_tool.h
2、vimer博友提出了用二级指针来管理
C/C++代码中,野指针问题历来已久,当然,大家都知道new/delete要成对出现:
然而现实中却并不是总是如此简单,考虑如下例子:
简单来说,即pA被赋值为NULL,对B中的m_pA没有产生影响,那么怎么才能产生影响呢?
我们有两个做法:
第一种,在A的析构函数里面去B.SetA(NULL),但是这个相当于A去操作了B的数据,这是不合理的。而且当外面的指针非常多的时候,也根本不可能实现。
第二种方法呢?是的,我们可以用二级指针。
考虑如下代码:
这样确实可以解决野指针的问题,但是同时也引入了另一个问题,那就是ppA本身该什么时候释放呢?答案是:当最后一个引用ppA的类释放掉的时候。
最后一个,对,我们可以使用引用计数!
OK,正式放出我们的代码,其中使用了引用计数来确定当最后一个类释放掉的时候,ppA指针的内存被析构:
我们来写段测试代码测试一下:
输出为:
这个类最有效的使用场景是当出现大量互指指针时,那么指向对象的指针有效性判断就尤其重要,而这个类可以完美解决这个问题。可能想的比较深的朋友会问,既然引用计数都已经用上了,那么为什么不直接通过引用计数来析构呢?
其实这几天我也在尝试,C++是否能引入完美的引用计数进行对象管理,而最终卡在一个地方,即:如果,在类的构造函数里面,需要将引用计数对象构造出来,那么引用计数就会出现问题,如:
这个时候就会出现问题,除非把Count构造的计数对象放到一个对象池中管理,但是又会增加对象查找的成本,所以最终放弃了这个想法。
另外一点就是,C/C++的指针在很多情况下是最方便的,过度的封装很可能会弄巧成拙,所以适度就好。
OK,惯例代码还是放到googlecode上:
http://code.google.com/p/vimercode/source/browse/#svn%2Ftrunk%2Fptr_proxy
目前代码使用中没有发现明显问题,欢迎大家交流~
3.还有egmkang网友提出的记录谁引用了自己这个方法:
实现最为简洁:
暂时准备按照自己的想法来做。
场景是这样:指针间互相引用,一个被删除的时候,另外一个不知道,它再使用的时候就会异常,因为此时指针已经为空了。
我今天提出的解决方案是:
所以实体都在一个地方管理,其他地方都保存其标识号,每次用的时候根据其标识取就行了,这样看起来最简单,保存的地方我们用hash_map,每次操作O(1)复杂度。
现在觉得还造不成hotpot。
不过刚才在网上,早就有网友遇到类似问题,并做了总结。
知然博友,在“安全编程-c++野指针和内存泄漏”提出的思路跟我的类似:
现在全文引用,备查:
摘要:
尽管C++ 野指针和内存泄漏一直被诟病,但是在实时性很强的应用场合,c++ 仍然是不二之选。游戏服务器开发仍然使用c++ 作为主语言,但是大多结合动态脚本技术,一方面规避了野指针和内存泄露,一方面获得了开发效率和扩展性的红利。但脚本技术不是本文的讨论重点,事实上关于c++ 与 lua的技术文章我也一直在整理中,将会另文别述。今天主要说说在使用c++过程中,如何避免和解决野指针和内存泄漏问题。
野指针:
野指针的出现会导致程序崩溃,这是每个人都不愿意看到的。Linux会生成coredump文件,可用gdb分析。Win下可以注册unexception获取调用堆栈,将错误信息写到文件中。先分析一下通常出现野指针的场景:class monster_t { protected: player_t* m_attack; public: void handle_ai() { if (m_attack) { int x = m_attack->get_x(); } } }
问题就在于,m_attack有值,但是对应的对象已经被销毁了。这是大部分野指针出现原因。分析类之间关系可知,monster_t 和 player_t是0-1的关系,monster_t引用player_t,但是player_t甚至都不知道有一个(或N个)monster 引用了自己。所以当player被销毁时,很难做到把所有引用该player_t的地方全部重置。这种问题其实比较常见,比如player中引用connection,而connection又是被网络层管理生命周期的,也同样容易产生野指针情况。常见的解决方式是:
class monster_t { protected: long m_attack_id; public: void handle_ai() { player_t* attack = obj_mgr.get(m_attack_id); if (attack) { int x = attack->get_x(); } } }
另外一种与之相似的方式:
class monster_t { protected: player_t* m_attack; public: void handle_ai() { if (obj_mgr.is_exist(m_attack)) { int x = m_attack->get_x(); } else { m_attack = NULL; } } }
梳理野指针的产生原因后,我们其实需要的是这样的指针:
一种指针,引用了另一个对象的地址(不然就不是指针了),当目标对象销毁时,该指针自然指向null,而不需要目标对象主动通知重置。
幸运的是,这种指针已经有了,就是weak_ptr; 在boost库中,sharedptr,scopedptr,weakptr统称为smartptr。可以尽量使用智能指针,避免野指针。本人建议尽量使用shared_ptr结合weak_ptr使用。Scoped_ptr本人使用的较少,只是在创建线程对象的时候使用,正好符合不能复制的语义。使用shared_ptr和weak_ptr的示例代码:
class monster_t { protected: weak_ptr<player_t> m_attack; shared_ptr<player_t> get_attack() { return shared_ptr<player_t>(m_attack); } public: void handle_ai() { shared_ptr<player_t> attack = get_attack(); if (attack) { int x = attack->get_x(); } } }
有人问monster_t为什么不直接使用shared_ptr,如果使用shared_ptr就不符合现实的模型了,monster_t显然不应该控制player_t的生命周期,如果使用了shared_ptr,那么可能导致player_t被延迟析构,甚至会导致内存暴涨。这也是shared_ptr的使用误区,所以本人建议尽量shared_ptr和weak_ptr结合用,否则野指针问题解决了,内存泄漏问题又来了。
内存泄漏:
野指针问题可以通过采用良好的编程范式,尽量规避,但总计c++规避内存泄漏的方法却很为难,简单而言尽量保证对象的分配和释放(分别)是单个入口的,这样大部分问题都可以拦截在code review阶段。那么怎么检测内存泄漏呢?首先说明本方法区别于valgrind等工具,该工具是调试期进行的检测,本文探究的是运行期的检测,确切说是运行期定时输出所有对象的数量到日志中。
首先定义分配、释放对象的接口:
template<typename T> T* new_obj() { T* p = new T(); singleton_t<obj_counter_t<T> >::instance().inc(1); return p; } template<typename T, typename ARG1> T* new_obj(ARG1 arg1) { T* p = new T(arg1); singleton_t<obj_counter_t<T> >::instance().inc(1); return p; } template<typename T, typename ARG1, typename ARG2> T* new_obj(ARG1 arg1, ARG2 arg2) { T* p = new T(arg1, arg2); singleton_t<obj_counter_t<T> >::instance().inc(1); return p; } template<typename T> T* new_array(int n) { T* p = new T ; singleton_t<obj_counter_t<T> >::instance().inc(n); return p; }
为了节省篇幅,这里只列举了三种构造的代码,当分配对象时,对应的类型数量增加1,obj_counter 使用原子操作为每一种类型记录其数量。
class obj_counter_i { public: obj_counter_i():m_ref_count(0){} virtual ~ obj_counter_i(){} void inc(int n) { (void)__sync_add_and_fetch(&m_ref_count, n); } void dec(int n) { __sync_sub_and_fetch(&m_ref_count, n); } long val() const{ return m_ref_count; } virtual string get_name() { return ""; } protected: volatile long m_ref_count; }; template<typename T> class obj_counter_t: public obj_counter_i { obj_counter_t() { singleton_t<obj_counter_t<T> >::instance().reg(this); } virtual string get_name() { return TYPE_NAME(T); } };
相应的当对象被释放的时候,对应的对象数量减一,示例代码如下:
template<typename T> void del_obj(T* p) { if (p) { delete p; singleton_t<obj_counter_t<T> >::instance().dec(1); } }
这样就做到了所有的对象的数量都被记录了,可以定时的将对象数量输出到文件:
class obj_counter_summary_t { public: void reg(obj_counter_i* p) { m_all_counter.push_back(p); } map<string, long> get_all_obj_num() { map<string, long> ret; for (list<obj_counter_i*>::iterator it = m_all_counter.begin(); it != m_all_counter.end(); ++it) { ret.insert(make_pair((*it)->get_name(), (*it)->val())); } return ret; } void dump(const string& path_) { ofstream tmp_fstream; tmp_fstream.open(path_.c_str()); map<string, long> ret = get_all_obj_num(); map<string, long>::iterator it = ret.begin(); time_t timep = time(NULL); struct tm *tmp = localtime(&timep); char tmp_buff[256]; sprintf(tmp_buff, "%04d%02d%02d-%02d:%02d:%02d", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); char buff[1024] = {0}; snprintf(buff, sizeof(buff), "obj,num,%s\n", tmp_buff); tmp_fstream << buff; for (; it != ret.end(); ++it) { snprintf(buff, sizeof(buff), "%s,%ld\n", it->first.c_str(), it->second); tmp_fstream << buff; } tmp_fstream.flush(); } protected: list<obj_counter_i*> m_all_counter; };
输出的文件格式为csv格式,方便进一步做数据分析。可以使用我开发的小工具格式化csv数据。url:http://ffown.sinaapp.com/perf/csv.html
文件内容data:
obj,num,20120606-17:01:41
dumy,1111
foo,222
obj,num,20120606-18:01:41
dumy,11311
foo,2422
obj,num,20120606-19:01:41
dumy,41111
foo,24442
总结:
野指针可以使用shared_ptr和weak_ptr结合使用来尽量规避。
使用shared_ptr要尽量小心,否则可能导致对象无法释放,导致内存泄漏。
可以定时输出当前所有对象的数量,来分析是否有内存泄漏,或者内存泄漏是有哪些对象引起的。
本文介绍了记录所有对象的方法,除了可以分析内存泄漏外,也不失为数据分析的一种方法。需要注明的是,本方法不能替代valgrind工具,二者作用不同。
TYPE_NAME 的实现参考
https://ffown.googlecode.com/svn/trunk/fflib/include/type_i.h
全部示例代码:https://ffown.googlecode.com/svn/trunk/fflib/include/obj_tool.h
2、vimer博友提出了用二级指针来管理
C/C++代码中,野指针问题历来已久,当然,大家都知道new/delete要成对出现:
123 | A *p = new A();delete p;p = NULL; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | classA { public: C(){} virtual~C(){} }; classB { public: B(){ m_pA=NULL; } virtual~B(){} voidSetA(A*p) { m_pA=p; } private: A*m_pA; }; A*pA=newA(); B*pB=newB(); pB->SetA(pA); deletepA; pA=NULL; //此时B中的m_pA已经无效,但是m_pA仍然不等于NULL,所以用 != NULL来判断不会有任何作用 |
我们有两个做法:
第一种,在A的析构函数里面去B.SetA(NULL),但是这个相当于A去操作了B的数据,这是不合理的。而且当外面的指针非常多的时候,也根本不可能实现。
第二种方法呢?是的,我们可以用二级指针。
考虑如下代码:
12345678910111213141516171819202122232425262728293031323334 | class A{public: C() {} virtual ~C() {}};class B{public: B() { m_ppA = NULL; } virtual ~B() {} void SetA(A** pp) { m_ppA = pp; } private: A** m_ppA;}; A** ppA = new (A*)(); (*ppA) = new A();B* pB = new B(); pB->SetA(ppA); delete (*ppA);(*ppA) = NULL; //这个时候,B中的m_ppA也会收到影响,即*m_ppA == NULL |
最后一个,对,我们可以使用引用计数!
OK,正式放出我们的代码,其中使用了引用计数来确定当最后一个类释放掉的时候,ppA指针的内存被析构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | /*============================================================================= # # FileName: ptr_proxy.h # Desc: 这个类的作用,就是为了解决互指指针,不知道对方已经析构的问题 # # Author: dantezhu # Email: zny2008@gmail.com # HomePage: http://www.vimer.cn # # Created: 2011-06-13 15:24:12 # Version: 0.0.1 # History: # 0.0.1 | dantezhu | 2011-06-13 15:24:12 | initialization # =============================================================================*/ #ifndef __PTR_PROXY_H__ #define __PTR_PROXY_H__ #include <stdio.h> #include <string.h> #include <stdint.h> #include <iostream> #include <memory> #include <sstream> #include <algorithm> #include <string> #include <vector> #include <set> #include <map> usingnamespacestd; template<typenameT> classptr_proxy { public: ptr_proxy(constT*pobj=NULL):m_ppobj(NULL),m_pcount(NULL) { if(pobj==NULL) { return; } init(pobj); } ptr_proxy(constptr_proxy&rhs)// 拷贝构造函数 { m_ppobj=rhs.m_ppobj;// 指向同一块内存 m_pcount=rhs.m_pcount;// 使用同一个计数值 add_count(); } virtual~ptr_proxy() { dec_count(); } /** * @brief 如果指向的对象被释放了,一定要调用这个函数让他知道 */ voidset2null() { if(m_ppobj) { (*m_ppobj)=NULL; } } /** * @brief 赋值函数 * * @param rhs 源对象 * * @return 自己的引用 */ ptr_proxy&operator=(constptr_proxy&rhs) { if(m_ppobj==rhs.m_ppobj)// 首先判断是否本来就指向同一内存块 return*this;// 是则直接返回 dec_count(); m_ppobj=rhs.m_ppobj;// 指向同一块内存 m_pcount=rhs.m_pcount;// 使用同一个计数值 add_count(); return*this;// 是则直接返回 } ptr_proxy&operator=(constT*pobj) { if(m_ppobj&&*m_ppobj==pobj)// 首先判断是否本来就指向同一内存块 return*this;// 是则直接返回 dec_count(); init(pobj); return*this; } /** * @brief 获取内部关联的obj的指针 * * @return */ T*true_ptr() { if(m_ppobj) { return*m_ppobj; } else { returnNULL; } } /** * @brief 获取内部关联的obj的指针 * * @return */ T*operator*() { returntrue_ptr(); } /** * @brief 获取内部关联的obj的个数 * * @return 个数 */ intcount() { if(m_pcount!=NULL) { return*m_pcount; } return0; } /** * @brief 判断智能指针是否为空 * * @return */ boolis_null() { if(m_ppobj==NULL||(*m_ppobj)==NULL) { returntrue; } returnfalse; } protected: voidinit(constT*pobj) { m_ppobj=new(T*)(); *m_ppobj=(T*)pobj; m_pcount=newint();// 初始化计数值为 1 *m_pcount=1; } voidadd_count() { if(m_pcount==NULL) { return; } (*m_pcount)++; } /** * @brief 计数减1 */ voiddec_count() { if(m_pcount==NULL||m_ppobj==NULL) { return; } (*m_pcount)--;// 计数值减 1 ,因为该指针不再指向原来内存块了 if(*m_pcount<=0)// 已经没有别的指针指向原来内存块了 { //我们不去主动析构对象 //free_sptr(*m_ppobj);//把对象析构 if(m_ppobj!=NULL) { deletem_ppobj; m_ppobj=NULL; } if(m_pcount!=NULL) { deletem_pcount; m_pcount=NULL; } } } protected: T**m_ppobj; int*m_pcount; }; template<typenameT> classIPtrProxy { public: IPtrProxy(){ m_ptr_proxy=(T*)this; } virtual~IPtrProxy(){ m_ptr_proxy.set2null(); } ptr_proxy<T>&get_ptr_proxy() { returnm_ptr_proxy; } protected: ptr_proxy<T>m_ptr_proxy; }; # |
输出为:
12 | isnullthisisprint |
其实这几天我也在尝试,C++是否能引入完美的引用计数进行对象管理,而最终卡在一个地方,即:如果,在类的构造函数里面,需要将引用计数对象构造出来,那么引用计数就会出现问题,如:
1 2 3 4 5 6 7 8 9 | classA { public: A(){ Countt(this); } virtual~A(){} }; Countc=newA(); |
另外一点就是,C/C++的指针在很多情况下是最方便的,过度的封装很可能会弄巧成拙,所以适度就好。
OK,惯例代码还是放到googlecode上:
http://code.google.com/p/vimercode/source/browse/#svn%2Ftrunk%2Fptr_proxy
目前代码使用中没有发现明显问题,欢迎大家交流~
3.还有egmkang网友提出的记录谁引用了自己这个方法:
实现最为简洁:
/*============================================================================= # # FileName: ptr_proxy.h # Desc: 这个类的作用,就是为了解决互指指针,不知道对方已经析构的问题 # # Author: dantezhu # Email: zny2008@gmail.com # HomePage: http://www.vimer.cn # # Created: 2011-06-13 15:24:12 # Version: 0.0.1 # History: # 0.0.1 | dantezhu | 2011-06-13 15:24:12 | initialization # =============================================================================*/ #ifndef __PTR_PROXY_H__ #define __PTR_PROXY_H__ #include <stdio.h> #include <string.h> #include <stdint.h> #include <iostream> #include <memory> #include <sstream> #include <algorithm> #include <string> #include <vector> #include <set> #include <map> using namespace std; template <typename T> class ptr_proxy { public: ptr_proxy(const T* pobj = NULL) : m_ppobj(NULL), m_pcount(NULL) { if (pobj == NULL) { return; } init(pobj); } ptr_proxy(const ptr_proxy& rhs) // 拷贝构造函数 { m_ppobj = rhs.m_ppobj; // 指向同一块内存 m_pcount = rhs.m_pcount; // 使用同一个计数值 add_count(); } virtual ~ptr_proxy() { dec_count(); } /** * @brief 如果指向的对象被释放了,一定要调用这个函数让他知道 */ void set2null() { if (m_ppobj) { (*m_ppobj) = NULL; } } /** * @brief 赋值函数 * * @param rhs 源对象 * * @return 自己的引用 */ ptr_proxy& operator=(const ptr_proxy& rhs) { if (m_ppobj == rhs.m_ppobj) // 首先判断是否本来就指向同一内存块 return *this; // 是则直接返回 dec_count(); m_ppobj = rhs.m_ppobj; // 指向同一块内存 m_pcount = rhs.m_pcount; // 使用同一个计数值 add_count(); return *this; // 是则直接返回 } ptr_proxy& operator=(const T* pobj) { if (m_ppobj && *m_ppobj == pobj) // 首先判断是否本来就指向同一内存块 return *this; // 是则直接返回 dec_count(); init(pobj); return *this; } /** * @brief 获取内部关联的obj的指针 * * @return */ T* true_ptr() { if (m_ppobj) { return *m_ppobj; } else { return NULL; } } /** * @brief 获取内部关联的obj的指针 * * @return */ T* operator*() { return true_ptr(); } /** * @brief 获取内部关联的obj的个数 * * @return 个数 */ int count() { if (m_pcount != NULL) { return *m_pcount; } return 0; } /** * @brief 判断智能指针是否为空 * * @return */ bool is_null() { if (m_ppobj == NULL || (*m_ppobj) == NULL) { return true; } return false; } protected: void init(const T* pobj) { m_ppobj = new (T*)(); *m_ppobj = (T*)pobj; m_pcount = new int(); // 初始化计数值为 1 *m_pcount = 1; } void add_count() { if (m_pcount == NULL) { return; } (*m_pcount)++; } /** * @brief 计数减1 */ void dec_count() { if (m_pcount == NULL || m_ppobj == NULL) { return; } (*m_pcount)--; // 计数值减 1 ,因为该指针不再指向原来内存块了 if (*m_pcount <= 0) // 已经没有别的指针指向原来内存块了 { //我们不去主动析构对象 //free_sptr(*m_ppobj);//把对象析构 if (m_ppobj != NULL) { delete m_ppobj; m_ppobj = NULL; } if (m_pcount != NULL) { delete m_pcount; m_pcount = NULL; } } } protected: T** m_ppobj; int* m_pcount; }; template <typename T> class IPtrProxy { public: IPtrProxy() { m_ptr_proxy = (T*)this; } virtual ~IPtrProxy() { m_ptr_proxy.set2null(); } ptr_proxy<T>& get_ptr_proxy() { return m_ptr_proxy; } protected: ptr_proxy<T> m_ptr_proxy; }; #endif
#include <stdio.h> #include <string.h> #include <stdint.h> #include <iostream> #include <memory> #include <sstream> #include <algorithm> #include <string> #include <vector> #include <set> #include <map> #include "test.h" #include "ptr_proxy.h" #include <assert.h> using namespace std; class object : public referable<object> { public: int x; int test(){ return x; } }; void test() { object *a = new object; object *a1 = new object; ref_ptr<object> b = a; a->x = 10; b->test(); assert(b && b->x == 10); delete a; assert(!b); b->test(); b = a1; assert(b); } int main(){ test(); }
暂时准备按照自己的想法来做。
相关文章推荐
- 关于C/C++中二维数组、指针的引用等若干问题的总结
- Qt C++ 指向对象的指针与内存分配的问题分析
- C/C++"野指针"问题总结
- C++技术问题总结-第9篇 智能指针
- 《剑指offer》面试题57 删除链表中重复的结点 C++ 实现 以及 错误总结 (指针问题)!!
- 内存泄漏、资源泄漏、空指针等问题的分析与总结
- C++ 二级指针、函数指针与数组复合类型的问题分析
- 【转载】同事对项目UI问题案例分析培训的总结
- 关于C++异常抛出指针问题的探讨
- VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结
- 前两天困扰我的一个关于指针和内存的问题的总结
- JAVA与C++::关于JNI中文字符串操作问题总结
- C、C++中指针加1的问题
- 详细的讲解C/C++指针和内存问题的文章
- m文件转换为C/C++文件的编译、绘图、参数、打包问题总结
- JAVA与C++::关于JNI中文字符串操作问题总结
- 关于c++ 函数指针的问题
- 指针操作超越变量作用范围的问题(高质量c++)
- C++经典指针问题[转帖]
- C/C++中空指针与0与NULL和其他若干问题小结。(转帖)