本文介绍了虚拟析构函数和删除具有多重继承的对象...它是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 首先,我明白为什么 virtual 析构函数在单继承和通过基指针删除对象方面是需要的。这尤其是关于多重继承和为什么这个工作原因的原因。这个问题出现在我的大学课程之一,没有人(包括教授)确定为什么这样工作: include< iostream> struct A { virtual〜A() { std :: cout< 〜A< std :: endl; } int memberA; }; struct B { virtual〜B() { std :: cout< 〜B<< std :: endl; } int memberB; }; struct AB:public A,public B { virtual〜AB() { std :: cout< 〜AB<< std :: endl; } }; int main() { AB * ab1 = new AB(); AB * ab2 = new AB(); A * a = ab1; B * b = ab2; 删除a; delete b; } 这个输出是:编译器如何知道如何调用 A '和 B 删除 a 或 b 时,具体来说,如何布局 AB 的内存(特别是它的虚拟函数表),这样 A code> B 析构函数可以调用? 我的教授建议内存布局如下: / p> AB + --------- + + ---- + | A VFT | - - - - - - > | 〜A | + --------- + + ---- + | memberA | + --------- + + ---- + | B VFT | - - - - - - > | 〜B | + --------- + + ---- + | memberB | + --------- + //我不知道〜AB会去哪里... 我们都很好奇这些析构函数是如何实际布局在内存中的,以及如何调用 delete code> a 或 b 会导致正确调用所有析构函数。删除一个基础对象在单继承(因为有一个单一的虚拟函数表使用)是有意义的,但显然我不能正确地理解事情,因为我不能理解单个继承版本并应用它解决方案 p> 实际上,编译器将隐式调用插入到〜A()和〜B()转换为〜AB()。该机制与单继承完全相同,除了有多个基本析构函数供编译器调用。 我认为你的图中的主要混乱来源是虚拟析构函数的多个单独的vtable条目。在实践中,将有一个条目指向〜A(),〜B() code>〜AB()用于 A , B 和 gcc 来编译代码,那么$ c> AB() $ c>并检查程序集,我看到〜AB()中的以下代码: LEHE0: movq -24(%rbp),%rax addq $ 16,%rax movq%rax,%rdi LEHB1: call __ZN1BD2Ev LEHE1: movq -24(%rbp),%rax movq%rax,%rdi LEHB2: call __ZN1AD2Ev 这会调用〜B(),后跟〜A()。 这三个类的虚拟表如下: ; A __ZTV1A: .quad 0 .quad __ZTI1A .quad __ZN1AD1Ev .quad __ZN1AD0Ev ; B __ZTV1B: .quad 0 .quad __ZTI1B .quad __ZN1BD1Ev .quad __ZN1BD0Ev ; AB __ZTV2AB: .quad 0 .quad __ZTI2AB .quad __ZN2ABD1Ev .quad __ZN2ABD0Ev .quad -16 .quad __ZTI2AB .quad __ZThn16_N2ABD1Ev .quad __ZThn16_N2ABD0Ev 指的是类的完全对象析构函数。对于 A ,这指向〜A()等。 First, I understand why virtual destructors are needed in terms of single inheritance and deleting an object through a base pointer. This is specifically about multiple inheritance and the reason behind why this works. This question came up in one of my university classes, and no one (including the professor) was sure why this worked:#include <iostream>struct A{ virtual ~A() { std::cout << "~A" << std::endl; } int memberA;};struct B{ virtual ~B() { std::cout << "~B" << std::endl; } int memberB;};struct AB : public A, public B{ virtual ~AB() { std::cout << "~AB" << std::endl; }};int main(){ AB* ab1 = new AB(); AB* ab2 = new AB(); A* a = ab1; B* b = ab2; delete a; delete b;}The output for this is:How does the compiler know how to call A's and B's destructor when deleting a or b? Specifically, how is the memory for AB laid out (particularly it's virtual function table), such that the A and B destructors can be called?My professor was suggesting that memory would be laid out (something) like this: AB+---------+ +----+| A VFT | - - - - - -> | ~A |+---------+ +----+| memberA |+---------+ +----+| B VFT | - - - - - -> | ~B |+---------+ +----+| memberB |+---------+// I have no idea where ~AB would go...We're all curious how these destructors are actually laid out in memory and how calling delete on either a or b results in all the destructors being properly called. It makes sense that deleting a base object works in single inheritance (because there's a single virtual function table to work with), but apparently I'm not understanding things correctly because I can't take my understanding of the single inheritance version and apply it to this multiple inheritance example.So how does this work? 解决方案 It works because the standard says that it works.In practice, the compiler inserts implicit calls to ~A() and ~B() into ~AB(). The mechanism is exactly the same as with single inheritance, except that there are multiple base destructors for the compiler to call.I think the main source of confusion in your diagram is the multiple separate vtable entries for the virtual destructor. In practice, there will be a single entry that would point to ~A(), ~B() and ~AB() for A, B and AB() respectively.For example, if I compile your code using gcc and examine the assembly, I see the following code in ~AB():LEHE0: movq -24(%rbp), %rax addq $16, %rax movq %rax, %rdiLEHB1: call __ZN1BD2EvLEHE1: movq -24(%rbp), %rax movq %rax, %rdiLEHB2: call __ZN1AD2EvThis calls ~B() followed by ~A().The virtual tables of the three classes look as follows:; A__ZTV1A: .quad 0 .quad __ZTI1A .quad __ZN1AD1Ev .quad __ZN1AD0Ev; B__ZTV1B: .quad 0 .quad __ZTI1B .quad __ZN1BD1Ev .quad __ZN1BD0Ev; AB__ZTV2AB: .quad 0 .quad __ZTI2AB .quad __ZN2ABD1Ev .quad __ZN2ABD0Ev .quad -16 .quad __ZTI2AB .quad __ZThn16_N2ABD1Ev .quad __ZThn16_N2ABD0EvFor each class, entry #2 refers to the class's "complete object destructor". For A, this points to ~A() etc. 这篇关于虚拟析构函数和删除具有多重继承的对象...它是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-16 03:41