2

通过引用计数解决野指针的问题(C&C++)

 2 years ago
source link: https://blogread.cn/it/article/3804?f=hot1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

通过引用计数解决野指针的问题(C&C++)

浏览:3080次  出处信息

    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: [email protected]
#     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 
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

建议继续学习:

QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK