您的位置:首页 > 文旅 > 旅游 > 十堰网站建设多少钱_电商平台的发展_淘宝关键词_如何制作简易网站

十堰网站建设多少钱_电商平台的发展_淘宝关键词_如何制作简易网站

2025/9/7 2:31:24 来源:https://blog.csdn.net/moter/article/details/143382258  浏览:    关键词:十堰网站建设多少钱_电商平台的发展_淘宝关键词_如何制作简易网站
十堰网站建设多少钱_电商平台的发展_淘宝关键词_如何制作简易网站

我们来看一个和派生类重载delete操作符相关的C++程序:

class animal {
public:virtual ~animal() {}
};class dog : public animal {
public:virtual ~dog() {puts("destory dog");}void operator delete(void* p) {printf("delete dog storage in %p\n", p);::operator delete(p);}
};int main(int argc, char** argv) {animal* ap = new dog;delete ap;return 0;
}

派生类dog继承基类animal,并且重载了operator delete()。ap 是一个基类animal的指针,但是它指向了一个由派生类dog在堆上创建的对象。那么,当程序执行delete ap时,会调用dog类重载的operator delete()吗?也就是说当delete一个基类指针时,会调用派生类重载的operator delete()函数吗?

不过需要注意的是,这里void dog::operator delete(void* p)并不是一个virtual函数,我们试着把它声明成virtual看看,编译时会发生失败:

error: 'operator delete' cannot be declared 'virtual', since it is always static

编译失败的原因是operator delete()总是类的static函数,也就是它不可能当作virtual函数的,也不是非static成员函数。况且也并没有在基类animal中定义一个operator delete()虚函数,然后在派生类dog中重写override这个函数,并不是我们日常编程实践中常见的OOP编程套路。因此,既然无法使用virtual函数来动态绑定,感觉应该是调用了全局的operator delete()函数。我们运行一下程序,它的输出log如下:

destory dog
delete dog storage in 0x10ad2b0

可见,当程序执行delete ap时,还是调用了dog类提供的operator delete(),这有点出乎意料,既然不是virtual函数,基类指针又是怎么知道派生类中的这个static成员函数的呢?

我们看一下汇编代码,下面是gcc在O1优化选项下生成的汇编代码:

dog::~dog() [base object destructor]:sub     rsp, 8mov     QWORD PTR [rdi], OFFSET FLAT:vtable for dog+16mov     edi, OFFSET FLAT:.LC0call    putsadd     rsp, 8ret
.LC1:.string "delete dog storage in %p\n"
dog::operator delete(void*):push    rbxmov     rbx, rdimov     rsi, rdimov     edi, OFFSET FLAT:.LC1mov     eax, 0call    printfmov     rdi, rbxcall    operator delete(void*)pop     rbxret
dog::~dog() [deleting destructor]:push    rbxmov     rbx, rdicall    dog::~dog() [complete object destructor]mov     rdi, rbxcall    dog::operator delete(void*)pop     rbxret
dog::dog() [base object constructor]:mov     QWORD PTR [rdi], OFFSET FLAT:vtable for dog+16ret
main:push    rbxmov     edi, 8call    operator new(unsigned long)mov     rbx, raxmov     rdi, raxcall    dog::dog() [complete object constructor]mov     rax, QWORD PTR [rbx]mov     rdi, rbxcall    [QWORD PTR [rax+8]]mov     eax, 0pop     rbxret

在main函数中语句delete ap对应的汇编代码是:

mov     rax, QWORD PTR [rbx]  //rbx是this指针,rax是虚函数表指针vptr
mov     rdi, rbx
call    [QWORD PTR [rax+8]]    // 调用虚函数中的第2个虚函数

核心指令call [QWORD PTR [rax+8]],它调用了虚函数表中的第2个虚函数,在这里rax是指向dog类虚函数表的vptr指针,[rax+0]指向第1个虚函数,[rax+8]指向第2个虚函数。下面是dog类虚函数表的信息:

vtable for dog:
.quad 0
.quad typeinfo for dog
.quad dog::~dog() [complete object destructor]
.quad dog::~dog() [deleting destructor]

第2个虚函数是:dog::~dog() [deleting destructor],它的汇编代码如下:

dog::~dog() [deleting destructor]:push    rbxmov     rbx, rdicall    dog::~dog() [complete object destructor]mov     rdi, rbxcall    dog::operator delete(void*)pop     rbxret

第6行指令:call dog::operator delete(void*),此处调用了dog类重载的operator delete()函数,因此程序最终还是调用了dog类重载的operator delete()函数,并没有调用全局的operator delete()。

我们知道在C++中,delete的语义是先调用对象的析构函数,然后再调用delete操作符函数,看一下dog::~dog() [deleting destructor]的实现流程:
第4行代码:call dog::~dog() [complete object destructor],在这里它和dog::~dog() [base object destructor]相同,它的汇编代码如下:

dog::~dog() [base object destructor]:sub     rsp, 8mov     QWORD PTR [rdi], OFFSET FLAT:vtable for dog+16mov     edi, OFFSET FLAT:.LC0call    putsadd     rsp, 8ret

它就是dog类的析构函数,可见函数dog::~dog() [deleting destructor]先调用了dog::~dog() [base object destructor],然后调用了call dog::operator delete(void*)。该函数先调用了dog类的析构函数,然后再调用dog类的重载的operator delete(),正好符合delete操作符的语义,也就是说在这里,编译器使用了一个独立的函数来封装了这个delete操作符的功能。可见,编译器生成了一个特殊的virtual析构函数,在这个析构函数中调用了operator delete()。

因此,派生类中重载的delete操作符在使用基类指针析构堆上对象时,也是动态绑定来调用的,只不过它并不是使用传统的方式,定义成虚函数来动态绑定的,而是被封装在一个编译器自动生成的虚析构函数中,通过动态绑定虚析构函数来间接的动态绑定。

我们再看一下虚函数表中的第1个虚函数:dog::~dog() [complete object destructor],它是dog类正常的析构函数,也就是程序中所定义的虚析构函数,它主要用于栈上对象和static对象的析构和在子类的析构函数中调用父类的析构函数,而第2个虚函数:dog::~dog() [deleting destructor],它是编译器为dog类新增的析构函数,主要用于delete操作符来析构堆上对象,这个函数用户并不可见,毕竟按照C++的语义,一个类只能有一个析构函数,故这个析构函数对用户是不可见的,只是编译器用来辅助进行对象的delete操作的,仅供编译器使用。

如果我们在测试程序中,编写下面的测试代码:

void foo() {dog d; // 创建栈上对象
}dog global; // 创建全局对象

编译器生成的汇编代码如下:

foo():sub     rsp, 24mov     QWORD PTR [rsp+8], OFFSET FLAT:vtable for dog+16lea     rdi, [rsp+8]call    dog::~dog() [complete object destructor]add     rsp, 24ret
__static_initialization_and_destruction_0():sub     rsp, 8mov     edx, OFFSET FLAT:__dso_handlemov     esi, OFFSET FLAT:global // 程序退出时,回调global的析构函数mov     edi, OFFSET FLAT:dog::~dog() [complete object destructor]call    __cxa_atexitadd     rsp, 8ret

可见,这两种创建类型的对象在析构时,都调用了正常实现的析构函数:dog::~dog() [complete object destructor]。

需要注意的是,这种动态绑定delete操作符的机制,是GCC和CLANG编译器所使用的方案,MSVC编译器并没有使用,它没有定义了一个新的析构函数,而是通过为析构函数传递不同标志参数的方式来实现的。

下面是MSVC编译器生成的汇编代码,传递的标志参数存放在edx寄存器中,具体细节可自己分析一下:

virtual void * dog::`scalar deleting destructor'(unsigned int) PROC                         ; dog::`scalar deleting destructor', COMDAT
$LN18:mov     QWORD PTR [rsp+8], rbxpush    rdisub     rsp, 32                             ; 00000020Hlea     rax, OFFSET FLAT:const dog::`vftable'mov     rbx, rcxmov     QWORD PTR [rcx], raxmov     edi, edx // 把标志参数存入edi寄存器lea     rcx, OFFSET FLAT:`string'call    putslea     rax, OFFSET FLAT:const animal::`vftable'mov     QWORD PTR [rbx], raxtest    dil, 1je      SHORT $LN4@scalar  // 参数edi的第0位为0时,不需要delete堆上内存test    dil, 4jne     SHORT $LN3@scalar  // 参数edi的第2位为0时,在22行调用dog重载的operator delete,否则在在27行调用全局的operator deletemov     rdx, rbxlea     rcx, OFFSET FLAT:`string'call    printfmov     rcx, rbxcall    void operator delete(void *)                     ; operator deletejmp     SHORT $LN4@scalar
$LN3@scalar:mov     edx, 8mov     rcx, rbxcall    void __global_delete(void *,unsigned __int64)         ; __global_delete
$LN4@scalar:mov     rax, rbxmov     rbx, QWORD PTR [rsp+48]add     rsp, 32                             ; 00000020Hpop     rdiret     0
virtual void * dog::`scalar deleting destructor'(unsigned int) ENDP  

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com