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


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

    #include<iostream>
    using namespace std;
    
    class A{
        int a;
    public:    ~A(){
            printf("delete A
    ");
        }
    };
    
    
    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
     
  • 相关阅读:
    Silverlight“.NET研究” 2.5D RPG游戏技巧与特效处理:(七)动画特写 狼人:
    Silverlight 2.5D RPG游戏“.NET研究”技巧与特效处理:(六)流光追影 狼人:
    WPF中使用amCh“.NET研究”arts绘制股票K线图 狼人:
    验证.N“.NET研究”ET强命称的思路和实例 狼人:
    ASP.NET“.NET研究”下用URLRewriter重写二级域名 狼人:
    spring初始化在ServletContextListener实现类中获取spring注入对象
    执行原因【菜鸟笔记】Ubuntu系统shellscript中 关于for循环以及declare出错的原因
    jqueryfunctionchrome浏览器不支持 onmouseleave事件的解决
    异步线程C#异步委托的使用
    android删除android拦截短信并删除该条短信
  • 原文地址:https://www.cnblogs.com/huhuuu/p/3490502.html
Copyright © 2020-2023  润新知