C++ 如何虚拟继承是在编译器中实现的吗?
编译器如何实现虚拟继承?
在以下代码中:
class A {
public:
A(int) {}
};
class B : public virtual A {
public:
B() : A(1) {}
};
class C : public B {
public:
C() : A(3), B() {}
};
编译器是否生成 B::ctor
函数的两个实例,一个没有 A(1)
调用,另一个有它?因此,当从派生类的构造函数调用 B::constructor
时,将使用第一个实例,否则使用第二个实例。
How the compilers implement the virtual inheritance?
In the following code:
class A {
public:
A(int) {}
};
class B : public virtual A {
public:
B() : A(1) {}
};
class C : public B {
public:
C() : A(3), B() {}
};
Does a compiler generate two instance of B::ctor
function, one without A(1)
call, and one with it? So when B::constructor
is called from derived class's constructor the first instance is used, otherwise the second.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
它依赖于实现。例如,GCC(请参阅这个问题)将发出两个构造函数,其中一个带有调用
A(1)
,另一个没有。当构造 B 时,将调用“完整”版本:
当构造 C 时,将调用基本版本:
事实上,即使没有虚拟继承,也会发出两个构造函数,并且它们将是相同的。
It's implementation-dependent. GCC (see this question), for example, will emit two constructors, one with a call to
A(1)
, another one without.When B is constructed, the "full" version is called:
When C is constructed, the base version is called instead:
In fact, two constructors will be emitted even if there is no virtual inheritance, and they will be identical.
编译器不会创建 B 的另一个构造函数 - 但它会忽略
A(1)
。由于A
实际上是继承的,因此首先使用其默认构造函数构造它。由于在调用B()
时它已经构造完毕,因此A(1)
部分将被忽略。编辑 - 我错过了
C
构造函数初始化列表中的A(3)
部分。当使用虚拟继承时,只有最底层的派生类才会初始化虚拟基类。因此A
将使用A(3)
而不是其默认构造函数来构造。其余部分仍然有效 - 中间类(此处为B
)对A
的任何初始化都将被忽略。编辑 2,尝试回答有关上述实现的实际问题:
在 Visual Studio(至少 2010)中,使用标志而不是
B()
的两个实现。由于B
实际上继承自A
,因此在调用A
的构造函数之前,会检查该标志。如果未设置该标志,则跳过对A()
的调用。然后,在从B
派生的每个类中,该标志在初始化A
后重置。如果C
是某个D
的一部分(如果D
继承自C
、D
将初始化A
)。The compiler does not create another constructor of B - but it ignores the
A(1)
. SinceA
is virtually inherited, it is constructed first, with its default constructor. And since it's already constructed whenB()
is invoked, theA(1)
part is ignored.Edit - I missed the
A(3)
part inC
's constructor initialization list. When virtual inheritance is used, only the most derived class initializes the virtual base classes. SoA
will be constructed withA(3)
and not its default constructor. The rest still stands - any initializations ofA
by an intermediate class (hereB
) are ignored.Edit 2, trying to answer the actual question regarding the implementation of the above:
In Visual Studio (at least 2010), a flag is used instead of having two implementations of
B()
. SinceB
virtually inherits fromA
, before it callsA
's constructor, the flag is checked. If the flag is not set, the call toA()
is skipped. Then, in every class deriving fromB
, the flag is reset after it initializesA
. The same mechanism is used to preventC
from initializingA
if it's part of someD
(ifD
inherits fromC
,D
will initializeA
).我建议你读一些论文。这两个非常有趣,尤其是第一个,因为它来自 C++ 之父:
[1] 比亚恩·斯特鲁斯特鲁普。 C++ 的多重继承。 C/C++ 用户
《期刊》,1999 年 5 月。
[2] J寺庙。多重继承实现的系统方法。
ACM SIGPLAN 公告,第 28 卷,1993 年 4 月 4 日。
在我的大学(作为学生)举办关于多重继承的研讨会时,我将它们作为主要参考资料。
I suggest you to read some papers. These two are really interesting, especially the first since it comes from C++'s father:
[1] Bjarne Stroustrup. Multiple Inheritance for C++. The C/C++ Users
Journal, May 1999.
[2] J. Templ. A Systematic Approach to Multiple Inheritance Implementation.
ACM SIGPLAN Notices, Volume 28, No. 4 April 1993.
I used them as main references while making a seminar (as a student) on multiple inheritance in my university.
Itanium C++ ABI 是解决所有问题的有用资源,例如“这怎么可能”由 C++ 编译器实现”。
特别是5.1.4其他特殊功能和实体列出了不同的特殊功能不同目的的成员函数:
1.1 Definitions 部分很有用(但不完整):
中,完整对象构造函数和基础对象构造函数的目的是显而易见的。
The Itanium C++ ABI is a useful resource for all questions like "how could this be implemented by C++ compilers".
In particular 5.1.4 Other Special Functions and Entities list different special member functions for different purposes:
The 1.1 Definitions section is useful (but not complete):
From these definitions, the purpose of the complete object constructor and of the base object constructor are obvious.
如前所述,这取决于编译器的实现。
但是,通常程序员每次添加新方法时,都会存储在代码中,即使存在另一个具有相同 id 的方法。其他地方(“覆盖”或“超载”)。
每个方法的代码仅存储一次,因此,如果一个类从父类继承并使用相同的方法,则在内部,它使用指向代码的指针,不会重复代码。
如果父类定义了虚拟方法,并且子类重写了该方法,则这两个方法都会被存储。每个类都有一个称为“虚拟方法表”的东西,其中有一个指向每个方法的指针表。
不用担心性能,编译器不会为方法重复代码。
As mentioned before, it depends on the compiler implementation.
But, usually each time a programmer adds a new method, is stored in code, even if there is another method with the same id. elsewhere ("overriden" or "overloaded").
The code for each method is stored only once, so if a class inherits and uses the same method from a parent class, internally, its uses a pointer to the code, it doesn't duplicates the code.
If a parent class defines a virtual method, and if a child class overrides it, both methods are stored. Each class has something called "Virtual Method Table" where there is a table of pointers to each method.
Don't worry about performance, the compiler doesn't duplicate code for methods.