C/C++代码中,野指针问题历来已久,当然,大家都知道new/delete要成对出现:
A *p = new A(); delete p; p = NULL;
然而现实中却并不是总是如此简单,考虑如下例子:
class A { public: C() {} virtual ~C() {} }; class B { public: B() { m_pA = NULL; } virtual ~B() {} void SetA(A* p) { m_pA = p; } private: A* m_pA; }; A* pA = new A(); B* pB = new B(); pB->SetA(pA); delete pA; pA = NULL; //此时B中的m_pA已经无效,但是m_pA仍然不等于NULL,所以用 != NULL来判断不会有任何作用
简单来说,即pA被赋值为NULL,对B中的m_pA没有产生影响,那么怎么才能产生影响呢?
我们有两个做法:
第一种,在A的析构函数里面去B.SetA(NULL),但是这个相当于A去操作了B的数据,这是不合理的。而且当外面的指针非常多的时候,也根本不可能实现。
第二种方法呢?是的,我们可以用二级指针。
考虑如下代码:
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
这样确实可以解决野指针的问题,但是同时也引入了另一个问题,那就是ppA本身该什么时候释放呢?答案是:当最后一个引用ppA的类释放掉的时候。
最后一个,对,我们可以使用引用计数!
OK,正式放出我们的代码,其中使用了引用计数来确定当最后一个类释放掉的时候,ppA指针的内存被析构:
/*============================================================================= # # 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 #include #include #include #include #include #include #include #include #include #include
我们来写段测试代码测试一下:
#include #include #include #include #include #include #include #include #include #include #include{ public: A() {} virtual ~A() {} }; class B : public IPtrProxy { public: B() {} virtual ~B() {} void print() { printf(\"this is print\\\\n\"); } void SetAPtr(const ptr_proxy& pptr) { m_ptr_a = pptr; } void check() { if (m_ptr_a.is_null()) { printf(\"is null\\\\n\"); } else { printf(\"is not null\\\\n\"); } } ptr_proxy m_ptr_a; }; int main(int argc, char **argv) { A* a = new A(); B* b = new B(); b->SetAPtr(a->get_ptr_proxy()); delete a; b->check(); b->get_ptr_proxy().true_ptr()->print(); delete b; return 0; }
输出为:
is null this is print
这个类最有效的使用场景是当出现大量互指指针时,那么指向对象的指针有效性判断就尤其重要,而这个类可以完美解决这个问题。
可能想的比较深的朋友会问,既然引用计数都已经用上了,那么为什么不直接通过引用计数来析构呢?
其实这几天我也在尝试,C++是否能引入完美的引用计数进行对象管理,而最终卡在一个地方,即:
如果,在类的构造函数里面,需要将引用计数对象构造出来,那么引用计数就会出现问题,如:
class A { public: A() { Count t(this); } virtual ~A() {} }; Count c = new A();
这个时候就会出现问题,除非把Count构造的计数对象放到一个对象池中管理,但是又会增加对象查找的成本,所以最终放弃了这个想法。
另外一点就是,C/C++的指针在很多情况下是最方便的,过度的封装很可能会弄巧成拙,所以适度就好。
OK,惯例代码还是放到googlecode上:
http://code.google.com/p/vimercode/source/browse/#svn%2Ftrunk%2Fptr_proxy
- 本文来自:Linux学习网