为什么基类析构函数可以自由调用

时间:2014-09-09 13:31:57

标签: c++ gcc embedded free destructor

我正在编写一个内存非常有限的NIOS II内核的C ++程序。由于它是一个嵌入式系统,我们也没有使用堆。由于我们在代码中添加了继承,因此我们看到malloc和free(以及相关函数)正在构建到我们的二进制文件中,从而将其大小增加了几千字节。

我们有一个纯虚拟基类,StatisticsApi和一个派生类Statistics。统计数据和它们都定义了虚拟析构函数。

我们正在使用带有fno-rtti和-fno_expections的Alteras GCC进行编译,并且还定义了我们自己的__cxa_pure_virtual()

仔细观察它,我们看到删除是从析构函数中调用的(因而是免费的)。这是为什么?它试图释放什么记忆?

这是析构函数的汇编程序:

0000c6b8 <_ZN7Namespace12StatisticsD0Ev>:
    c6b8:   defffd04    addi    sp,sp,-12
    c6bc:   dfc00215    stw ra,8(sp)
    c6c0:   df000115    stw fp,4(sp)
    c6c4:   df000104    addi    fp,sp,4
    c6c8:   e13fff15    stw r4,-4(fp)
    c6cc:   00c000b4    movhi   r3,2
    c6d0:   18c74b04    addi    r3,r3,7468
    c6d4:   e0bfff17    ldw r2,-4(fp)
    c6d8:   10c00015    stw r3,0(r2)
    c6dc:   e13fff17    ldw r4,-4(fp)
    c6e0:   000c7f00    call    c7f0 <_ZN7Namespace15StatisticsApiD2Ev>
    c6e4:   00800044    movi    r2,1
    c6e8:   10803fcc    andi    r2,r2,255
    c6ec:   1005003a    cmpeq   r2,r2,zero
    c6f0:   1000021e    bne r2,zero,c6fc <_ZN7LinCtrl12TxStatisticsD0Ev+0x44>
    c6f4:   e13fff17    ldw r4,-4(fp)
    c6f8:   000c8e00    call    c8e0 <_ZdlPv>
    c6fc:   e037883a    mov sp,fp
    c700:   dfc00117    ldw ra,4(sp)
    c704:   df000017    ldw fp,0(sp)
    c708:   dec00204    addi    sp,sp,8
    c70c:   f800283a    ret


0000c710 <_ZN7Namespace12StatisticsD1Ev>:
    c710:   defffd04    addi    sp,sp,-12
    c714:   dfc00215    stw ra,8(sp)
    c718:   df000115    stw fp,4(sp)
    c71c:   df000104    addi    fp,sp,4
    c720:   e13fff15    stw r4,-4(fp)
    c724:   00c000b4    movhi   r3,2
    c728:   18c74b04    addi    r3,r3,7468
    c72c:   e0bfff17    ldw r2,-4(fp)
    c730:   10c00015    stw r3,0(r2)
    c734:   e13fff17    ldw r4,-4(fp)
    c738:   000c7f00    call    c7f0 <_ZN7Namespace15StatisticsApiD2Ev>
    c73c:   0005883a    mov r2,zero
    c740:   10803fcc    andi    r2,r2,255
    c744:   1005003a    cmpeq   r2,r2,zero
    c748:   1000021e    bne r2,zero,c754 <_ZN7Namespace12StatisticsD1Ev+0x44>
    c74c:   e13fff17    ldw r4,-4(fp)
    c750:   000c8e00    call    c8e0 <_ZdlPv>
    c754:   e037883a    mov sp,fp
    c758:   dfc00117    ldw ra,4(sp)
    c75c:   df000017    ldw fp,0(sp)
    c760:   dec00204    addi    sp,sp,8
    c764:   f800283a    ret


0000c768 <_ZN7Namespace12StatisticsD2Ev>:
    c768:   defffd04    addi    sp,sp,-12
    c76c:   dfc00215    stw ra,8(sp)
    c770:   df000115    stw fp,4(sp)
    c774:   df000104    addi    fp,sp,4
    c778:   e13fff15    stw r4,-4(fp)
    c77c:   00c000b4    movhi   r3,2
    c780:   18c74b04    addi    r3,r3,7468
    c784:   e0bfff17    ldw r2,-4(fp)
    c788:   10c00015    stw r3,0(r2)
    c78c:   e13fff17    ldw r4,-4(fp)
    c790:   000c7f00    call    c7f0 <_ZN7Namespace15StatisticsApiD2Ev>
    c794:   0005883a    mov r2,zero
    c798:   10803fcc    andi    r2,r2,255
    c79c:   1005003a    cmpeq   r2,r2,zero
    c7a0:   1000021e    bne r2,zero,c7ac <_ZN7Namespace12StatisticsD2Ev+0x44>
    c7a4:   e13fff17    ldw r4,-4(fp)
    c7a8:   000c8e00    call    c8e0 <_ZdlPv>
    c7ac:   e037883a    mov sp,fp
    c7b0:   dfc00117    ldw ra,4(sp)
    c7b4:   df000017    ldw fp,0(sp)
    c7b8:   dec00204    addi    sp,sp,8
    c7bc:   f800283a    ret

以下是基类的析构函数:

0000c7f0 <_ZN7Namespace15StatisticsApiD2Ev>:
    c7f0:   defffd04    addi    sp,sp,-12
    c7f4:   dfc00215    stw ra,8(sp)
    c7f8:   df000115    stw fp,4(sp)
    c7fc:   df000104    addi    fp,sp,4
    c800:   e13fff15    stw r4,-4(fp)
    c804:   00c000b4    movhi   r3,2 
    c808:   18c76204    addi    r3,r3,7560 
    c80c:   e0bfff17    ldw r2,-4(fp)
    c810:   10c00015    stw r3,0(r2)
    c814:   0005883a    mov r2,zero
    c818:   10803fcc    andi    r2,r2,255
    c81c:   1005003a    cmpeq   r2,r2,zero
    c820:   1000021e    bne r2,zero,c82c <_ZN7Namespace15StatisticsApiD2Ev+0x3c>
    c824:   e13fff17    ldw r4,-4(fp)
    c828:   000c8e00    call    c8e0 <_ZdlPv>
    c82c:   e037883a    mov sp,fp
    c830:   dfc00117    ldw ra,4(sp)
    c834:   df000017    ldw fp,0(sp)
    c838:   dec00204    addi    sp,sp,8
    c83c:   f800283a    ret


0000c840 <_ZN7LinCtrl15TxStatisticsApiD0Ev>:
    c840:   defffd04    addi    sp,sp,-12
    c844:   dfc00215    stw ra,8(sp)
    c848:   df000115    stw fp,4(sp)
    c84c:   df000104    addi    fp,sp,4
    c850:   e13fff15    stw r4,-4(fp)
    c854:   00c000b4    movhi   r3,2
    c858:   18c76204    addi    r3,r3,7560
    c85c:   e0bfff17    ldw r2,-4(fp)
    c860:   10c00015    stw r3,0(r2)
    c864:   00800044    movi    r2,1
    c868:   10803fcc    andi    r2,r2,255
    c86c:   1005003a    cmpeq   r2,r2,zero
    c870:   1000021e    bne r2,zero,c87c <_ZN7Namespace15StatisticsApiD0Ev+0x3c>
    c874:   e13fff17    ldw r4,-4(fp)
    c878:   000c8e00    call    c8e0 <_ZdlPv>
    c87c:   e037883a    mov sp,fp
    c880:   dfc00117    ldw ra,4(sp)
    c884:   df000017    ldw fp,0(sp)
    c888:   dec00204    addi    sp,sp,8
    c88c:   f800283a    ret


0000c890 <_ZN7Namespace15StatisticsApiD1Ev>:
    c890:   defffd04    addi    sp,sp,-12
    c894:   dfc00215    stw ra,8(sp)
    c898:   df000115    stw fp,4(sp)
    c89c:   df000104    addi    fp,sp,4
    c8a0:   e13fff15    stw r4,-4(fp)
    c8a4:   00c000b4    movhi   r3,2
    c8a8:   18c76204    addi    r3,r3,7560
    c8ac:   e0bfff17    ldw r2,-4(fp)
    c8b0:   10c00015    stw r3,0(r2)
    c8b4:   0005883a    mov r2,zero
    c8b8:   10803fcc    andi    r2,r2,255
    c8bc:   1005003a    cmpeq   r2,r2,zero
    c8c0:   1000021e    bne r2,zero,c8cc <_ZN7Namespace15StatisticsApiD1Ev+0x3c>
    c8c4:   e13fff17    ldw r4,-4(fp)
    c8c8:   000c8e00    call    c8e0 <_ZdlPv>
    c8cc:   e037883a    mov sp,fp
    c8d0:   dfc00117    ldw ra,4(sp)
    c8d4:   df000017    ldw fp,0(sp)
    c8d8:   dec00204    addi    sp,sp,8
    c8dc:   f800283a    ret

最后删除:

0000c8e0 <_ZdlPv>:
    c8e0:   20000126    beq r4,zero,c8e8 <_ZdlPv+0x8>
    c8e4:   000ebf01    jmpi    ebf0 <free>
    c8e8:   f800283a    ret

以下是NIOS II指令集的链接供参考:http://www.altera.com/literature/hb/nios2/n2cpu_nii51017.pdf

我们找到的两个解决方案/解决方法是:

  1. 将new / delete重新定义为空函数(没有堆,所以它们不应该 无论如何都要被召唤!)因此避免了对自由的呼唤。

  2. 在C ++中使析构函数未定义,这使得它们无法获取 在汇编程序中实例化。虽然编译器会抱怨 没有虚拟析构函数。

  3. 解决方法与否,为什么析构函数是免费的?它想要释放什么记忆!?!我们有其他析构函数既不是基类也不是派生类,它们不会自由调用。这就是这样的析构函数的样子:

    00005194 <_ZN7Namespace16OtherClassD2Ev>:
        5194:   defffe04    addi    sp,sp,-8
        5198:   df000115    stw fp,4(sp)
        519c:   df000104    addi    fp,sp,4
        51a0:   e13fff15    stw r4,-4(fp)
        51a4:   e037883a    mov sp,fp
        51a8:   df000017    ldw fp,0(sp)
        51ac:   dec00104    addi    sp,sp,4
        51b0:   f800283a    ret
    
    000051b4 <_ZN7Namespace16OtherClassD1Ev>:
        51b4:   defffe04    addi    sp,sp,-8
        51b8:   df000115    stw fp,4(sp)
        51bc:   df000104    addi    fp,sp,4
        51c0:   e13fff15    stw r4,-4(fp)
        51c4:   e037883a    mov sp,fp
        51c8:   df000017    ldw fp,0(sp)
        51cc:   dec00104    addi    sp,sp,4
        51d0:   f800283a    ret
    

    而且,一般来说,为什么每个析构函数(D0,D1和D2)都有多个函数?

1 个答案:

答案 0 :(得分:1)

当您使用虚拟析构函数调用对象上的delete时,它会在虚拟表中查找要调用的函数。该函数调用析构函数以及释放内存,因为只有在派生级别它才知道传递给自由函数的正确指针。

问题是编译器不知道你从不调用delete。它仍然必须创建代码,即使它可能永远不会被使用,就像任何其他虚函数一样。

尽管作为一般规则,析构函数应该在具有其他虚函数的类中是虚拟的,但这可能是一个有效的例外。在基类中保护析构函数有助于避免错误。