虚函数和虚析构函数
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.
虚析构函数的汇编分析
总结:在释放一个指向子类的基类指针时,如果基类具有虚析构函数,则“最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用”。
相关文章推荐
- C++虚析构函数、虚函数结合考题变种
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数
- C++的虚函数和虚析构函数
- 虚函数实现多态,虚析构函数,虚函数表和多态实现机制,纯虚函数
- 深入探究C++中虚函数和虚析构函数的实现原理
- C++虚析构函数、虚函数结合考题变种
- 虚析构函数、虚函数
- C++ 虚析构函数-(一个带有虚函数功能的类,则它需要一个虚析构函数)
- 虚函数的使用 以及虚函数与重载的关系, 空虚函数的作用,纯虚函数->抽象类,基类虚析构函数使释放对象更彻底
- 虚函数与虚析构函数原理
- 虚函数和虚析构函数的实现原理--虚函数表
- 虚函数与虚析构函数
- C++学习之多态篇(虚函数和虚析构函数的实现原理--虚函数表)
- 与虚函数相关的几点注意之二——虚析构函数
- 虚函数,虚析构函数,纯虚函数,抽象类
- gdb/lldb C++类的虚析构函数在虚函数表的数目为2?
- 虚析构函数(删除基类对象时,先调用派生类的虚构函数,再调用基类虚析构函数)
- 虚析构函数、虚函数结合考题变种
- 虚函数、虚析构函数、虚函数表
- 虚函数继承、虚继承、虚析构函数、纯虚函数