您的位置:首页 > 其它

虚函数和虚析构函数

2010-04-16 21:19 197 查看

1.
虚析构函数简介

虚析构函数的使用非常普遍。如QT中的QObject:
class Q_CORE_EXPORT QObject

{

Q_OBJECT

Q_PROPERTY(QString objectName READ
objectName WRITE setObjectName)

Q_DECLARE_PRIVATE(QObject)

public:

Q_INVOKABLE explicit
QObject(QObject *parent=0);

virtual ~QObject();

...

}

虚析构函数的具体作用我所理解的就是避免内存泄露。
但是虚析构函数和虚函数在运行时期行为不一样,有其自己的特殊性。

2.
虚析构例子

如下面的例子:
/*Sample1*/

#include<iostream>

#include<stdlib.h>

using namespace std;

class Base

{

public:

Base() {};

virtual ~Base() { cout <<
"Destructor of Base!" << endl; };

virtual void funcA() { cout
<< "funcA of Base!" << endl; };

};

class Derived : public Base

{

public:

Derived() {};

~Derived() { cout <<
"Destructor of Derived!" << endl; };

void funcA() { cout <<
"funcA of Derived!" << endl; };

};

int main()

{

Base *pderived =new Derived;

pderived->funcA();

delete pderived;

return 0;

}

运行后输出:
funcA
of Derived!

Destructor
of Derived!

Destructor
of Base!

可以看到,虚函数只是调用了子类的虚函数。而父类和子类的虚析构函数都被调用了!
虚函数的调用通常只会调用到一个函数。而虚析构函数,则是“最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用”。这句话援引自
《effective C++》
14款。全文是“这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。”

3.
虚析构函数的汇编分析

但是到目前为止,我们仅仅知道有这么一回事。但是,为什么是这么一回事?
把上面的代码修改下,输出其汇编:
/*Sample2*/

using namespace std;

class Base

{

public:

//Base()
{};

virtual
~Base() {};// { cout << "Destructor of Base!" << endl; };

virtual
void funcA() {};// { cout << "funcA in class Base!" <<
endl; };

};

class Derived : public Base

{

public:

//Derived()
{};

~Derived()
{}; // { cout << "Destructor of Derived!" << endl; };

void
funcA() {}; //{ cout <<
"funcA in class Derived!" << endl; };

};

int main()

{

Base
*pderived =new Derived;

//pderived->funcA();

delete
pderived;

return 0;

}

对上述代码,针对是否使用了语句delete pderived使用g++
-S分别输出其汇编代码。
首先是没有调用delete pderived的汇编代码:
main:

.LFB968:

.cfi_startproc

.cfi_personality 0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset 8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register 5

andl $-16, %esp

pushl %ebx

subl $44, %esp

movl $4, (%esp)

.cfi_escape 0x10,0x3,0x7,0x55,0x9,0xf0,0x1a,0x9,0xfc,0x22

call _Znwj

movl %eax, %ebx

movl %ebx, %eax

movl %eax, (%esp)

call
_ZN7DerivedC1Ev


movl %ebx, %eax

movl %eax, 28(%esp)

movl $0, %eax

.L38:

addl $44, %esp

popl %ebx

movl %ebp, %esp

popl %ebp

ret

.cfi_endproc

调用了delete pderived的汇编代码:
main:

.LFB968:

.cfi_startproc

.cfi_personality 0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset 8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register 5

andl $-16, %esp

pushl %ebx

subl $44, %esp

movl $4, (%esp)

.cfi_escape 0x10,0x3,0x7,0x55,0x9,0xf0,0x1a,0x9,0xfc,0x22

call _Znwj

movl %eax, %ebx

movl %ebx, %eax

movl %eax, (%esp)

call
_ZN7DerivedC1Ev


movl %ebx, %eax

movl %eax, 28(%esp)

cmpl $0, 28(%esp)

je .L39

.L40:

.L38:

movl 28(%esp), %eax

movl (%eax), %eax

addl $4, %eax

movl (%eax), %edx

movl 28(%esp), %eax

movl %eax, (%esp)

call
*%edx


.L39:

movl $0, %eax

addl $44, %esp

popl %ebx

movl %ebp, %esp

popl %ebp

ret

.cfi_endproc

注意到这两段代码有一个区别是,没有调用delete pderived的汇编代码仅仅使用了一次call指令(call _ZN7DerivedC1Ev),而使用了两次(call
_ZN7DerivedC1Ev, call *%edx
)。
由此我们可以推断出,delete pderived的操作由call *%edx触发的。
那么,call *%edx到底做了些什么呢?

3.1
call *%edx

先附上代码,后续补上。

.file "virtual.cpp"

.local _ZStL8__ioinit

.comm _ZStL8__ioinit,1,1

.section .rodata

.LC0:

.string "Destructor of Derived!"

.text

.globl _Z5hellov

.type _Z5hellov, @function

_Z5hellov:

.LFB957:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

movl $.LC0, 4(%esp)

movl $_ZSt4cout, (%esp)

call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)

movl %eax, (%esp)

call _ZNSolsEPFRSoS_E

leave

ret

.cfi_endproc

.LFE957:

.size _Z5hellov, .-_Z5hellov

.section .text._ZN4BaseD2Ev,"axG",@progbits,_ZN4BaseD2Ev,comdat

.align 2

.weak _ZN4BaseD2Ev

.type _ZN4BaseD2Ev, @function

_ZN4BaseD2Ev:

.LFB959:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

movl 8(%ebp), %eax

movl $_ZTV4Base+8, (%eax)

movl $0, %eax

testb %al, %al

je .L6

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZdlPv

.L6:

leave

ret

.cfi_endproc

.LFE959:

.size _ZN4BaseD2Ev, .-_ZN4BaseD2Ev

.section .text._ZN4BaseD1Ev,"axG",@progbits,_ZN4BaseD1Ev,comdat

.align 2

.weak _ZN4BaseD1Ev

.type _ZN4BaseD1Ev, @function

_ZN4BaseD1Ev:

.LFB960:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

movl 8(%ebp), %eax

movl $_ZTV4Base+8, (%eax)

movl $0, %eax

testb %al, %al

je .L10

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZdlPv

.L10:

leave

ret

.cfi_endproc

.LFE960:

.size _ZN4BaseD1Ev, .-_ZN4BaseD1Ev

.section .text._ZN4BaseD0Ev,"axG",@progbits,_ZN4BaseD0Ev,comdat

.align 2

.weak _ZN4BaseD0Ev

.type _ZN4BaseD0Ev, @function

_ZN4BaseD0Ev:

.LFB961:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

movl 8(%ebp), %eax

movl $_ZTV4Base+8, (%eax)

movl $1, %eax

testb %al, %al

je .L14

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZdlPv

.L14:

leave

ret

.cfi_endproc

.LFE961:

.size _ZN4BaseD0Ev, .-_ZN4BaseD0Ev

.section .text._ZN4Base5funcAEv,"axG",@progbits,_ZN4Base5funcAEv,comdat

.align 2

.weak _ZN4Base5funcAEv

.type _ZN4Base5funcAEv, @function

_ZN4Base5funcAEv:

.LFB962:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

popl %ebp

ret

.cfi_endproc

.LFE962:

.size _ZN4Base5funcAEv, .-_ZN4Base5funcAEv

.globl _Unwind_Resume

.section .text._ZN7DerivedD1Ev,"axG",@progbits,_ZN7DerivedD1Ev,comdat

.align 2

.weak _ZN7DerivedD1Ev

.type _ZN7DerivedD1Ev, @function

_ZN7DerivedD1Ev:

.LFB965:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

.cfi_lsda
0x0,.LLSDA965

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

pushl %esi

pushl %ebx

subl $16, %esp

movl 8(%ebp), %eax

movl $_ZTV7Derived+8, (%eax)

.LEHB0:

.cfi_offset
3, -16

.cfi_offset
6, -12

call _Z5hellov

.LEHE0:

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZN4BaseD2Ev

movl $0, %eax

testb %al, %al

je .L21

jmp .L23

.L22:

.L19:

movl %edx, %ebx

movl %eax, %esi

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZN4BaseD2Ev

movl %esi, %eax

movl %ebx, %edx

movl %eax, (%esp)

.LEHB1:

call _Unwind_Resume

.LEHE1:

.L23:

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZdlPv

.L21:

addl $16, %esp

popl %ebx

popl %esi

popl %ebp

ret

.cfi_endproc

.LFE965:

.size _ZN7DerivedD1Ev, .-_ZN7DerivedD1Ev

.globl __gxx_personality_v0

.section .gcc_except_table,"a",@progbits

.LLSDA965:

.byte 0xff

.byte 0xff

.byte 0x1

.uleb128
.LLSDACSE965-.LLSDACSB965

.LLSDACSB965:

.uleb128
.LEHB0-.LFB965

.uleb128
.LEHE0-.LEHB0

.uleb128
.L22-.LFB965

.uleb128
0x0

.uleb128
.LEHB1-.LFB965

.uleb128
.LEHE1-.LEHB1

.uleb128
0x0

.uleb128
0x0

.LLSDACSE965:

.section .text._ZN7DerivedD1Ev,"axG",@progbits,_ZN7DerivedD1Ev,comdat

.section .text._ZN7DerivedD0Ev,"axG",@progbits,_ZN7DerivedD0Ev,comdat

.align 2

.weak _ZN7DerivedD0Ev

.type _ZN7DerivedD0Ev, @function

_ZN7DerivedD0Ev:

.LFB966:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

.cfi_lsda
0x0,.LLSDA966

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

pushl %esi

pushl %ebx

subl $16, %esp

movl 8(%ebp), %eax

movl $_ZTV7Derived+8, (%eax)

.LEHB2:

.cfi_offset
3, -16

.cfi_offset
6, -12

call _Z5hellov

.LEHE2:

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZN4BaseD2Ev

movl $1, %eax

testb %al, %al

je .L28

jmp .L30

.L29:

.L26:

movl %edx, %ebx

movl %eax, %esi

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZN4BaseD2Ev

movl %esi, %eax

movl %ebx, %edx

movl %eax, (%esp)

.LEHB3:

call _Unwind_Resume

.LEHE3:

.L30:

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZdlPv

.L28:

addl $16, %esp

popl %ebx

popl %esi

popl %ebp

ret

.cfi_endproc

.LFE966:

.size _ZN7DerivedD0Ev, .-_ZN7DerivedD0Ev

.section .gcc_except_table

.LLSDA966:

.byte 0xff

.byte 0xff

.byte 0x1

.uleb128
.LLSDACSE966-.LLSDACSB966

.LLSDACSB966:

.uleb128
.LEHB2-.LFB966

.uleb128
.LEHE2-.LEHB2

.uleb128
.L29-.LFB966

.uleb128
0x0

.uleb128
.LEHB3-.LFB966

.uleb128
.LEHE3-.LEHB3

.uleb128
0x0

.uleb128
0x0

.LLSDACSE966:

.section .text._ZN7DerivedD0Ev,"axG",@progbits,_ZN7DerivedD0Ev,comdat

.section .text._ZN7Derived5funcAEv,"axG",@progbits,_ZN7Derived5funcAEv,comdat

.align 2

.weak _ZN7Derived5funcAEv

.type _ZN7Derived5funcAEv, @function

_ZN7Derived5funcAEv:

.LFB967:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

popl %ebp

ret

.cfi_endproc

.LFE967:

.size _ZN7Derived5funcAEv, .-_ZN7Derived5funcAEv

.section .text._ZN4BaseC2Ev,"axG",@progbits,_ZN4BaseC2Ev,comdat

.align 2

.weak _ZN4BaseC2Ev

.type _ZN4BaseC2Ev, @function

_ZN4BaseC2Ev:

.LFB971:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

movl 8(%ebp), %eax

movl $_ZTV4Base+8, (%eax)

popl %ebp

ret

.cfi_endproc

.LFE971:

.size _ZN4BaseC2Ev, .-_ZN4BaseC2Ev

.section .text._ZN7DerivedC1Ev,"axG",@progbits,_ZN7DerivedC1Ev,comdat

.align 2

.weak _ZN7DerivedC1Ev

.type _ZN7DerivedC1Ev, @function

_ZN7DerivedC1Ev:

.LFB974:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

movl 8(%ebp), %eax

movl %eax, (%esp)

call _ZN4BaseC2Ev

movl 8(%ebp), %eax

movl $_ZTV7Derived+8, (%eax)

leave

ret

.cfi_endproc

.LFE974:

.size _ZN7DerivedC1Ev, .-_ZN7DerivedC1Ev

.text

.globl main

.type main, @function

main:

.LFB968:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

andl $-16, %esp

pushl %ebx

subl $44, %esp

movl $4, (%esp)

.cfi_escape
0x10,0x3,0x7,0x55,0x9,0xf0,0x1a,0x9,0xfc,0x22

call _Znwj

movl %eax, %ebx

movl %ebx, %eax

movl %eax, (%esp)

call _ZN7DerivedC1Ev

movl %ebx, %eax

movl %eax, 28(%esp)

cmpl $0, 28(%esp)

je .L39

.L40:

.L38:

movl 28(%esp), %eax

movl (%eax), %eax

addl $4, %eax

movl (%eax), %edx

movl 28(%esp), %eax

movl %eax, (%esp)

call *%edx

.L39:

movl $0, %eax

addl $44, %esp

popl %ebx

movl %ebp, %esp

popl %ebp

ret

.cfi_endproc

.LFE968:

.size main, .-main

.weak _ZTV7Derived

.section .rodata._ZTV7Derived,"aG",@progbits,_ZTV7Derived,comdat

.align 8

.type _ZTV7Derived, @object

.size _ZTV7Derived, 20

_ZTV7Derived:

.long 0

.long _ZTI7Derived

.long _ZN7DerivedD1Ev

.long _ZN7DerivedD0Ev

.long _ZN7Derived5funcAEv

.weak _ZTV4Base

.section .rodata._ZTV4Base,"aG",@progbits,_ZTV4Base,comdat

.align 8

.type _ZTV4Base, @object

.size _ZTV4Base, 20

_ZTV4Base:

.long 0

.long _ZTI4Base

.long _ZN4BaseD1Ev

.long _ZN4BaseD0Ev

.long _ZN4Base5funcAEv

.weak _ZTS7Derived

.section .rodata._ZTS7Derived,"aG",@progbits,_ZTS7Derived,comdat

.type _ZTS7Derived, @object

.size _ZTS7Derived, 9

_ZTS7Derived:

.string "7Derived"

.weak _ZTI7Derived

.section .rodata._ZTI7Derived,"aG",@progbits,_ZTI7Derived,comdat

.align 4

.type _ZTI7Derived, @object

.size _ZTI7Derived, 12

_ZTI7Derived:

.long _ZTVN10__cxxabiv120__si_class_type_infoE+8

.long _ZTS7Derived

.long _ZTI4Base

.weak _ZTS4Base

.section .rodata._ZTS4Base,"aG",@progbits,_ZTS4Base,comdat

.type _ZTS4Base, @object

.size _ZTS4Base, 6

_ZTS4Base:

.string "4Base"

.weak _ZTI4Base

.section .rodata._ZTI4Base,"aG",@progbits,_ZTI4Base,comdat

.align 4

.type _ZTI4Base, @object

.size _ZTI4Base, 8

_ZTI4Base:

.long _ZTVN10__cxxabiv117__class_type_infoE+8

.long _ZTS4Base

.text

.type _Z41__static_initialization_and_destruction_0ii,
@function

_Z41__static_initialization_and_destruction_0ii:

.LFB983:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

cmpl $1, 8(%ebp)

jne .L44

cmpl $65535, 12(%ebp)

jne .L44

movl $_ZStL8__ioinit, (%esp)

call _ZNSt8ios_base4InitC1Ev

movl $_ZNSt8ios_base4InitD1Ev, %eax

movl $__dso_handle, 8(%esp)

movl $_ZStL8__ioinit, 4(%esp)

movl %eax, (%esp)

call __cxa_atexit

.L44:

leave

ret

.cfi_endproc

.LFE983:

.size _Z41__static_initialization_and_destruction_0ii,
.-_Z41__static_initialization_and_destruction_0ii

.type _GLOBAL__I__Z5hellov, @function

_GLOBAL__I__Z5hellov:

.LFB984:

.cfi_startproc

.cfi_personality
0x0,__gxx_personality_v0

pushl %ebp

.cfi_def_cfa_offset
8

movl %esp, %ebp

.cfi_offset
5, -8

.cfi_def_cfa_register
5

subl $24, %esp

movl $65535, 4(%esp)

movl $1, (%esp)

call _Z41__static_initialization_and_destruction_0ii

leave

ret

.cfi_endproc

.LFE984:

.size _GLOBAL__I__Z5hellov, .-_GLOBAL__I__Z5hellov

.section .ctors,"aw",@progbits

.align 4

.long _GLOBAL__I__Z5hellov

.weakref _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once

.weakref _ZL27__gthrw_pthread_getspecificj,pthread_getspecific

.weakref _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific

.weakref _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create

.weakref _ZL20__gthrw_pthread_joinmPPv,pthread_join

.weakref _ZL21__gthrw_pthread_equalmm,pthread_equal

.weakref _ZL20__gthrw_pthread_selfv,pthread_self

.weakref _ZL22__gthrw_pthread_detachm,pthread_detach

.weakref _ZL22__gthrw_pthread_cancelm,pthread_cancel

.weakref _ZL19__gthrw_sched_yieldv,sched_yield

.weakref _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock

.weakref _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock

.weakref _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock

.weakref _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock

.weakref _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init

.weakref _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy

.weakref _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast

.weakref _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal

.weakref _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait

.weakref _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait

.weakref _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy

.weakref _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create

.weakref _ZL26__gthrw_pthread_key_deletej,pthread_key_delete

.weakref _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init

.weakref _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype

.weakref _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy

.ident "GCC: (Ubuntu 4.4.1-4ubuntu9)
4.4.1"

.section .note.GNU-stack,"",@progbits

4.
虚析构函数的汇编分析

总结:在释放一个指向子类的基类指针时,如果基类具有虚析构函数,则“最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用”。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: