雕刻时光

just do it……nothing impossible
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

'scalar deleting destructor' 和 'vector deleting destructor'的区别

Posted on 2013-12-25 13:43  huhuuu  阅读(6600)  评论(0编辑  收藏  举报

在用到delete的时候,我们往往会针对类对象与类对象数组做不同删除,在这背后编译器是如何做的?

#include<iostream>
using namespace std;

class A{
    int a;
public:    ~A(){
        printf("delete A\n");
    }
};


int main(){
    A *pa = new A ;
    A *pas = new A[10] ;
    //delete  []pas;                //详细流程
    //delete []pa;                //会发生什么
    //delete pas;                    //会怎么样

    getchar();
}

 

从汇编的角度来看:在C++的delete与delete[]对应'scalar deleting destructor'或 'vector deleting destructor'

void scalar_deleting_destructor(A* pa)
{
pa->~A();
A::operator delete(pa);
}
void vector_deleting_destructor(A* pa, size_t count)
{
for (size_t i = 0; i < count; ++i)
pa[i].~A();
A::operator delete[](pa);
}
//delete  []pas;                //会调用10次析构函数在free
//delete []pa;                //超出范围的内存会被收回,VS: 编译正确,运行出错

//delete pas; //会怎么样,VS: 编译通过,运行报错 VC:编译通过,可以运行,但只调用一次析构函数


一下用于VS的解释:
参考

好,现在讨论在VS下用delete删除一个对象数组指针时报错的问题。

根据报错,我们会跟到一个dbgdel.cpp文件中,

  /* verify block type */
  _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

 

 

先看一个数据结构定义:就是DEBUG下系统对内存进行管理的一个数据结构:

 

_CrtMemBlockHeader * pHead;

typedef struct _CrtMemBlockHeader
{
        struct _CrtMemBlockHeader * pBlockHeaderNext;
        struct _CrtMemBlockHeader * pBlockHeaderPrev;
        char *                      szFileName;
        int                         nLine;
 #ifdef _WIN64
        /* These items are reversed on Win64 to eliminate gaps in the struct
         * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
         * maintained in the debug heap.
         */
        int                         nBlockUse;
        size_t                      nDataSize;
#else  /* _WIN64 */
        size_t                      nDataSize;
        int                         nBlockUse;
#endif  /* _WIN64 */
         long                        lRequest;
        unsigned char               gap[nNoMansLandSize];
        /* followed by:
         *  unsigned char           data[nDataSize];
         *  unsigned char           anotherGap[nNoMansLandSize];
         */
} _CrtMemBlockHeader;

可以看出来,这是一个双向的链表,(每一个结构中都包含一个pre和next指针)。 还包含如下信息: 申请空间的文件名、所在行数、用户申请的数据大小, 内存块的类型,用于内存管理信息的额外空间。

现在来看出错的断言:

 

 /* verify block type */
 _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

我们传进去的参数, pHead->nBlockUse的值为147 看看是如何判断一个内存块的是否是合法的。

#define _BLOCK_TYPE_IS_VALID(use) (_BLOCK_TYPE(use) == _CLIENT_BLOCK || \
                                              (use) == _NORMAL_BLOCK || \
                                   _BLOCK_TYPE(use) == _CRT_BLOCK    || \
                                              (use) == _IGNORE_BLOCK)

 

而BLOCK_TYPE(use)又是怎么的宏定义呢:

 

#define _BLOCK_TYPE(block)  (block & 0xFFFF)

 

我们的参数穿进去之后,还是147啊。 而
CLIENT_BLOCK    4
NORMAL_BLOCK    1
CRT_BLOCK       2
IGNORE_BLOCK    3

 

没一个条件能成立,所以断言不成立,系统就会弹出那么大的错误提示框。
 
但是为什么我们穿进去的参数会是147呢?
参考:http://m.oschina.net/blog/55763