指针的安全使用

 

1 指针的使用场合 Cases using pointers 

    在正常情况下,无论从安全性和编程效率、运行效率上比较,指针比普通声明的变量都要低,特别是new和delete的速度相当慢,因此一般不使用指针变量。以下是COODBLib优化前的几个例子:

在Debug版本中选取2E4个CCustomer过程中,需花费:
    563.414ms完成300032次CObject::operator new()操作
    469.955ms完成140000次delete()操作
而全面优化过后,进行此操作共耗时6~7秒,可见指针空间申请和销毁可能占用大量时间。

    但因为对于多态、引用、变量交换等操作指针有其优势,在此类情况下将不得不使用指针;此时要特别注意安全问题和内存回收问题。

2 指针的安全使用 Using pointers safely 

    以上三条就是指针的使用要点。

初始化

  • 在指针声明时应习惯性初始化为NULL,如
    CRecordset *pRecordset=NULL;
  • 在类的构造器中应对所有成员指针变量初始化。

检查 (Check state and parameters)

  • 检查函数传入参数,如
    BOOL SetName(LPCTSTR lpszName)
        {
        if (lpszName==NULL) 
            return FALSE;
        ...
        }
  • 在类成员函数中检查类的指针变量是否可用,即用于外部服务的指针变量是否已经就位。
  • 删除指针内存时检查指针是否为空,配合下面的复位指针,可防止多次删除同一个指针内存。

    一种例外情况是VC的Collection族类(Array/List/Map)使用时不需要判断,因为其GetAt()函数已经进行了判断。

复位指针

在删除指针内存后,应复位指针为0,否则在下次检查指针是否为空时可能出错。如:
    delete m_pRecordset;
    m_pRecordset=NULL;

但在脱离作用域前可不复位,如在函数末尾删除临时指针变量及在销毁器删除成员指针变量。

3 指针内存回收 Freeing memory of pointers 

    函数内指针

    如果此指针是临时变量或尽管是类的变量,却只在此函数中才会使用,在函数退出时删除,如果函数退出的情况很复杂,则使用函数销毁器来完成,否则极可能发生漏删现象。参考函数结构规范中的例子。

    替别的函数销毁指针是及其危险且难以维护的事情,如:

    void main()
    {
        char *sz=new char[8];
        memset(sz, 0, 8);
        memcpy(sz, "name", 4);
        PrintfName(sz);
        //...
    }

    void PrintName(char *sz)
    {
        printf("%s", sz);
        delete []sz;
    }

    当上面的两个函数不是同一个程序员维护时,极易发生内存泄漏、重复删除内存等问题。

    在函数中回收内存的要点在于:谁申请的内存谁回收。

    类成员指针

    删除类的内存的要点在于“谁的指针谁回收”,而不是“谁申请的内存谁回收”。因而类只负责回收自己的指针,而不管其父类和支类(CCustomer对于CGroup而言是一个支类)的指针内存是否回收。这使得类的内存回收变得简单,即只需要关心当前类有哪些成员指针变量即可。

    类成员指针变量的内存回收一般在销毁器中进行,少量情况下需要在销毁之前删除内存的,可建立FreeMemory()函数,但需要在销毁器中也调用此函数。注意FreeMemoryAll()和销毁器的要求一般略有不同,FreeMemoryAll()是销毁器的一个子集。COODBQuery是个典型的例子。

    不过具体到每个成员指针变量比较复杂,有几种情况:

    ①指针是外部服务的地址,此指针不需要删除,在VC的CRecordset类应用中可看到:

CDatabase Database;
CRecordset *pRecordset=new CRecordset(&Database);

    可以看出此类中必有一个指针来存储&Database,但此指针肯定不需要删除内存的操作。

    为了能清除的发现此类指针,推荐用m_cp作为其前缀,表明此为Const Pointer。

    ②指针是内部存储数据,则需要删除。内部存储数据有两种,一种是普通指针,由类的某个函数(如构造器)申请内存;另外一种是PtrArray/List/Map的元素,在类的外部申请,通过Add函数加入,但也在类的销毁器中回收。例如:

template <class Class>
COODBQuery <Class>::~COODBQuery()
    {
    if (m_pOODBFields)
        delete []m_pOODBFields;
    m_pOODBFields=NULL;
    FreeMemoryAll();
    }

template <class Class>
void COODBQuery <Class>::FreeMemoryAll()
    {
    //cy: Free all memory.
    for (int i=m_ClassArray.GetSize()-1;i>=0;i--)
        delete m_ClassArray.GetAt(i);
    
    RemoveAll(); //cy: This function will transfer class pointers from ClassArray to ClearedArray.
    }

    可以看到,遵循“谁的指针谁回收”的原则,尽管m_ClassArray.GetAt(i)(实际上是一个从COODBRecord继承的类的指针,如CCustomer)中可能还有指针变量,但其回收工作已经交由Class销毁器完成。这样封装的好处是无论Class的内部结构如何,是否发生变化,只要在Class销毁器中做好内存回收工作即可。

    全局指针,静态指针

    由于全局指针首先是一个全局变量,所以一般情况下会随着程序结束而自动被释放内存,但为了在debug下不会因此而混淆程序中其他未得到回收的内存,建议进行内存回收,其位置在ExitInstance()函数内进行。

    大部分全局指针都是静态指针,所以在命名上没有特殊要求,用g_p作为前缀即可。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=226736

我是最近几个月去做嵌入式开发,开始实际用c/c++去编程的.所以对指针的理解也只是最近才形成的,私下认为指针是c/c++中永远的痛,自然也是它最值得骄傲的地方.当我们去搜索用指针可能带来那些危险的时候,发现使用错误的情况真是千奇百怪,防不胜防...无论是在堆上还是在栈上都会有各式各样的错误问题,更多是我们很难察觉到问题的存在,以至于我在最后不得不写一个内存检查程序,到现在我都想自己去写一个内存管理程序去完全阻止哪些可怕的事情的发生.如下是我认为可能出现的错误情况(零时写的,不全).
在堆上:
向一个已被分配的地址再次分配对象或者数据;对同一个已分配的地址连续删除两次;不小心破坏分配内存块的头结构(比如溢出导致);重复分配或者删除一个在使用中的内存块中的某一个地址;内存数据被另一个指针由于溢出而冲掉;忘记删除已分配的内存(内存泄漏);即使您每次都检查了分配对象/数据块后的指针是否为空,但可能您没有抛出例外或者返回,以至在后面同样引用了空指针.
在栈上:
除了一般的指针错误外,还应该特别注意,当函数返回后此函数对应的栈所有数据块全部自动销毁,栈上的指针自然也消失.在栈上分配对象或者数据块时并非总是安全的,因为栈的大小有限,所以可能导致栈溢出(嵌入式尤其如此).
以上是我认为可能发生的错误,其实大部分都被我发生过拉,特别有一次数据溢出把类事例的另一个变量给冲掉,结果在调用另一个引用此变量的函数时老出错,这个错误让我查了好长时间,记忆犹新!!!!
以上经验仅供参考...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值