C++ 如何虚拟继承是在编译器中实现的吗?

发布于 2024-12-03 21:14:38 字数 360 浏览 4 评论 0原文

编译器如何实现虚拟继承?

在以下代码中:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

寻梦旅人 2024-12-10 21:14:38

它依赖于实现。例如,GCC(请参阅这个问题)将发出两个构造函数,其中一个带有调用A(1),另一个没有。

B1()
B2() // no A

当构造 B 时,将调用“完整”版本:

B1():
    A(1)
    B() body

当构造 C 时,将调用基本版本:

C():
    A(3)
    B2()
       B() body
    C() body

事实上,即使没有虚拟继承,也会发出两个构造函数,并且它们将是相同的。

It's implementation-dependent. GCC (see this question), for example, will emit two constructors, one with a call to A(1), another one without.

B1()
B2() // no A

When B is constructed, the "full" version is called:

B1():
    A(1)
    B() body

When C is constructed, the base version is called instead:

C():
    A(3)
    B2()
       B() body
    C() body

In fact, two constructors will be emitted even if there is no virtual inheritance, and they will be identical.

dawn曙光 2024-12-10 21:14:38

编译器不会创建 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 继承自CD 将初始化 A)。

The compiler does not create another constructor of B - but it ignores the A(1). Since A is virtually inherited, it is constructed first, with its default constructor. And since it's already constructed when B() is invoked, the A(1) part is ignored.

Edit - I missed the A(3) part in C's constructor initialization list. When virtual inheritance is used, only the most derived class initializes the virtual base classes. So A will be constructed with A(3) and not its default constructor. The rest still stands - any initializations of A by an intermediate class (here B) 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(). Since B virtually inherits from A, before it calls A's constructor, the flag is checked. If the flag is not set, the call to A() is skipped. Then, in every class deriving from B, the flag is reset after it initializes A. The same mechanism is used to prevent C from initializing A if it's part of some D (if D inherits from C, D will initialize A).

西瑶 2024-12-10 21:14:38

我建议你读一些论文。这两个非常有趣,尤其是第一个,因为它来自 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.

想念有你 2024-12-10 21:14:38

Itanium C++ ABI 是解决所有问题的有用资源,例如“这怎么可能”由 C++ 编译器实现”。

特别是5.1.4其他特殊功能和实体列出了不同的特殊功能不同目的的成员函数:

; ::= C1 # 完整的对象构造函数
             ::= C2 # 基础对象构造函数
             ::= C3 # 完整的对象分配构造函数
             ::= D0 # 删除析构函数
             ::= D1 # 完整的对象析构函数
             ::= D2 # 基础对象析构函数

1.1 Definitions 部分很有用(但不完整):

类 T 的基本对象析构函数

运行 T 的非静态数据成员和 T 的非虚拟直接基类的析构函数的函数。

T类的完整对象析构函数

除了基对象析构函数所需的操作之外,还运行虚拟基类的析构函数的函数
T.

删除类 T 的析构函数

除了完整对象析构函数所需的操作之外,该函数还调用适当的释放函数(即。
T 的运算符删除)。

中,完整对象构造函数和基础对象构造函数的目的是显而易见的。

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:

<ctor-dtor-name> ::= C1   # complete object constructor
             ::= C2   # base object constructor
             ::= C3   # complete object allocating constructor
             ::= D0   # deleting destructor
             ::= D1   # complete object destructor
             ::= D2   # base object destructor

The 1.1 Definitions section is useful (but not complete):

base object destructor of a class T

A function that runs the destructors for non-static data members of T and non-virtual direct base classes of T.

complete object destructor of a class T

A function that, in addition to the actions required of a base object destructor, runs the destructors for the virtual base classes
of T.

deleting destructor of a class T

A function that, in addition to the actions required of a complete object destructor, calls the appropriate deallocation function (i.e,.
operator delete) for T.

From these definitions, the purpose of the complete object constructor and of the base object constructor are obvious.

半世晨晓 2024-12-10 21:14:38

如前所述,这取决于编译器的实现。

但是,通常程序员每次添加新方法时,都会存储在代码中,即使存在另一个具有相同 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文