C++ 死角

Author: 徐艺波(xuyibo) Views: 497 UpdateTime:2007-8-9
      留言

  1. 介绍
  2. 我过去很是不喜欢C++这门语言,当看到有些人评价C++语言差的时候,我总是拍手称快。上个星期重读Steve Lippman《The Inside C++ Object Model》和《ATL开发指南》后,我才打心底下改变了对C++的看法。《The Inside C++ Object Model》里有句话,大体意思就是:“C++适合开发大型软件,但这也是有代价的,开发者需要去了解C++。”,看过去我就是站在C以及排斥C++各种花俏功能(模版、多重继承、虚函数、运算符重载)的角度上,来评价C++的。好了,一句话结束介绍“海纳百川,故成其大”,言归正传。

    这篇文章主要是针对介绍VC 2005编译器,介绍C++的死角,一个容易出错、不容易理解、而有每天都会碰到n次的地方。

  3. 对于基础数据类型,delete和delete[]生成代码一样
  4. 在VC下,基础数据类型,int、float、double等,delete和delete[]生成的代码一样。但对于删除new分配的数组,用delete[]删除是个好习惯,我不知道Linux和Unix下C++的运行库是如何设计。有知道的,可以回个贴。

    VC的C/C++运行库在每次调用new的时候记录了分配数据的大小,new返回的指针是一个_CrtMemBlockHeader数据结构。微软这么设计,估计是可以方便的检查出程序退出后的内存泄漏。为了节省你的时间,和增加印象,我将new和delete的相关代码摘录出来,红色标记的为要害。

    /*
    * For diagnostic purpose, blocks are allocated with extra information and
    * stored in a doubly-linked list.  This makes all blocks registered with
    * how big they are, when they were allocated, and what they are used for.
    */

    #define nNoMansLandSize 4

    typedef  struct  _CrtMemBlockHeader
    {
        struct  _CrtMemBlockHeader  *  pBlockHeaderNext;
        struct  _CrtMemBlockHeader  *  pBlockHeaderPrev;
        char  *                      szFileName;
        int                         nLine;
        size_t                      nDataSize;
        int                         nBlockUse;
        long                        lRequest;
        unsigned  char               gap[nNoMansLandSize];
        /* followed by:
        *  unsigned char           data[nDataSize];
        *  unsigned char           anotherGap[nNoMansLandSize];
        */
    }  _CrtMemBlockHeader;

    #define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
    #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)

    void  operator  delete(void  *pUserData)
    {
        _CrtMemBlockHeader  *  pHead;
        RTCCALLBACK(_RTC_Free_hook,  (pUserData,  0));

        if  (pUserData  ==  NULL)
            return;

        _mlock(_HEAP_LOCK);  /* block other threads */
        __TRY
            /* get a pointer to memory block header */
            pHead  =  pHdr(pUserData);
        /* verify block type */
        _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
        _free_dbg(  pUserData,  pHead->nBlockUse  );

        __FINALLY
            _munlock(_HEAP_LOCK);  /* release other threads */
        __END_TRY_FINALLY

            return;
    }

    void  *__CRTDECL  operator  new(size_t  size)  _THROW1(_STD  bad_alloc)
    {
        // try to allocate size bytes
        void  *p;
        while  ((p  =  malloc(size))  ==  0)  {
            if  (_callnewh(size)  ==  0)  {       
                // report no memory
                static  const  std::bad_alloc  nomem;
                _RAISE(nomem);
            }
        }
        return  (p);
    }

    extern  “C”  _CRTIMP  void  *  __cdecl  malloc(size_t  nSize)
    {
        void  *res  =  _nh_malloc_dbg(nSize,  _newmode,  _NORMAL_BLOCK,  NULL,  0);
        RTCCALLBACK(_RTC_Allocate_hook,  (res,  nSize,  0));
        return  res;
    }

    extern  “C”  void  *  __cdecl  _heap_alloc_dbg(
        size_t  nSize,
        int  nBlockUse,
        const  char  *  szFileName,
        int  nLine
        )
    {
        long  lRequest;
        size_t  blockSize;
        int  fIgnore  =  FALSE;
        _CrtMemBlockHeader  *  pHead;
        void  *retval=NULL;

        /* lock the heap
        */
        _mlock(_HEAP_LOCK);
        __try  {

            /* verify heap before allocation */
            if  (check_frequency  >  0)
                if  (check_counter  ==  (check_frequency    1))
                {
                    _ASSERTE(_CrtCheckMemory());
                    check_counter  =  0;
                }
                else
                    check_counter++;

            lRequest  =  _lRequestCurr;

            /* break into debugger at specific memory allocation */
            if  (_crtBreakAlloc  !=  1L  &&  lRequest  ==  _crtBreakAlloc)
                _CrtDbgBreak();

            /* forced failure */
            if  ((_pfnAllocHook)  &&  !(*_pfnAllocHook)(_HOOK_ALLOC,  NULL,  nSize,  nBlockUse,  lRequest,  (const  unsigned  char  *)szFileName,  nLine))
            {
                if  (szFileName)
                    _RPT2(_CRT_WARN,  “Client hook allocation failure at file %hs line %d.n”,
                    szFileName,  nLine);
                else
                    _RPT0(_CRT_WARN,  “Client hook allocation failure.n”);
            }
            else
            {
                /* cannot ignore CRT allocations */
                if  (_BLOCK_TYPE(nBlockUse)  !=  _CRT_BLOCK  &&
                    !(_crtDbgFlag  &  _CRTDBG_ALLOC_MEM_DF))
                    fIgnore  =  TRUE;

                /* Diagnostic memory allocation from this point on */

                if  (nSize  >  (size_t)(_HEAP_MAXREQ    nNoMansLandSize    sizeof(_CrtMemBlockHeader)))
                {
                    _RPT1(_CRT_ERROR,  “Invalid allocation size: %Iu bytes.n”,  nSize);
                    errno  =  ENOMEM;
                }
                else
                {
                    if  (!_BLOCK_TYPE_IS_VALID(nBlockUse))
                    {
                        _RPT0(_CRT_ERROR,  “Error: memory allocation: bad memory block type.n”);
                    }

                    blockSize  =  sizeof(_CrtMemBlockHeader)  +  nSize  +  nNoMansLandSize;

    #ifndef WINHEAP
                    /* round requested size */
                    blockSize  =  _ROUND2(blockSize,  _GRANULARITY);
    #endif  /* WINHEAP */

                    RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));
                    pHead  =  (_CrtMemBlockHeader  *)_heap_alloc_base(blockSize);

                    if  (pHead  ==  NULL)
                    {
                        errno  =  ENOMEM;
                        RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
                    }
                    else
                    {

                        /* commit allocation */
                        ++_lRequestCurr;

                        if  (fIgnore)
                        {
                            pHead->pBlockHeaderNext  =  NULL;
                            pHead->pBlockHeaderPrev  =  NULL;
                            pHead->szFileName  =  NULL;
                            pHead->nLine  =  IGNORE_LINE;
                            pHead->nDataSize  =  nSize;
                            pHead->nBlockUse  =  _IGNORE_BLOCK;
                            pHead->lRequest  =  IGNORE_REQ;
                        }
                        else  {
                            /* keep track of total amount of memory allocated */
                            _lTotalAlloc  +=  nSize;
                            _lCurAlloc  +=  nSize;

                            if  (_lCurAlloc  >  _lMaxAlloc)
                                _lMaxAlloc  =  _lCurAlloc;

                            if  (_pFirstBlock)
                                _pFirstBlock->pBlockHeaderPrev  =  pHead;
                            else
                                _pLastBlock  =  pHead;

                            pHead->pBlockHeaderNext  =  _pFirstBlock;
                            pHead->pBlockHeaderPrev  =  NULL;
                            pHead->szFileName  =  (char  *)szFileName;
                            pHead->nLine  =  nLine;
                            pHead->nDataSize  =  nSize;
                            pHead->nBlockUse  =  nBlockUse;
                            pHead->lRequest  =  lRequest;

                            /* link blocks together */
                            _pFirstBlock  =  pHead;
                        }

                        /* fill in gap before and after real block */
                        memset((void  *)pHead->gap,  _bNoMansLandFill,  nNoMansLandSize);
                        memset((void  *)(pbData(pHead)  +  nSize),  _bNoMansLandFill,  nNoMansLandSize);

                        /* fill data with silly value (but non-zero) */
                        memset((void  *)pbData(pHead),  _bCleanLandFill,  nSize);

                        RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));

                        retval=(void  *)pbData(pHead);
                    }
                }
            }
        }
        __finally  {
            /* unlock the heap
            */
            _munlock(_HEAP_LOCK);
        }
        return  retval;
    }

    __forceinline  void  *  __cdecl  _heap_alloc  (size_t  size)
    {
        void  *pvReturn;

        if  (_crtheap  ==  0)  {
            _FF_MSGBANNER();    /* write run-time error banner */
            _NMSG_WRITE(_RT_CRT_NOTINIT);  /* write message */
            __crtExitProcess(255);  /* normally _exit(255) */
        }

        if  (__active_heap  ==  __SYSTEM_HEAP)  {
            return  HeapAlloc(_crtheap,  0,  size  ?  size  :  1);
        }  else  {
            if  (  __active_heap  ==  __V6_HEAP  )  {
                if  (pvReturn  =  V6_HeapAlloc(size))  {
                    return  pvReturn;
                }
            }

            if  (size  ==  0)
                size  =  1;

            size  =  (size  +  BYTES_PER_PARA    1)  &  ~(BYTES_PER_PARA    1);
            return  HeapAlloc(_crtheap,  0,  size);

        }
    }

  5. 类型转换
  6. 在C++中,将一个指针转换为另一个指针,结果是多少?如果你和我一样,是个C好手,那么你肯定不加思考的说就是这个指针的值。但知道昨天晚上我在次看COM QueryInterface代码的实现,我puzzle了。很显然,结果肯定不是指针本身了,否则QueryInterface也不用这么多if语句来判断了,而且返回的接口肯定会出问题的。

    含有多个vptr表格的类,将this指针转换为其中虚函数表类型,VC编译器将根据继承顺序来调整this指针,以返回this指针中相应接口的vptr地址。

留言: (留言使用了AJAX,提交的留言将自动显示在下面)Top
User:

Email:beyond5776@163.com QQ:85521028
Copyright ©2002-2007 XuYibo All rights reserved. License | Contributor