“在哪里?”;虚拟的;在复杂的多重继承层次结构中是否需要关键字? 我理解C++虚拟继承的基础知识。但是,我不知道在复杂的类层次结构中到底需要在哪里使用virtual关键字。例如,假设我有以下类: A / \ B C / \ / \ D E F \ / \ / G H \ / I
如果我想确保这些类在任何子类中都不会出现一次以上,那么哪些基类需要标记为“在哪里?”;虚拟的;在复杂的多重继承层次结构中是否需要关键字? 我理解C++虚拟继承的基础知识。但是,我不知道在复杂的类层次结构中到底需要在哪里使用virtual关键字。例如,假设我有以下类: A / \ B C / \ / \ D E F \ / \ / G H \ / I,c++,multiple-inheritance,virtual-inheritance,C++,Multiple Inheritance,Virtual Inheritance,如果我想确保这些类在任何子类中都不会出现一次以上,那么哪些基类需要标记为virtual?都是吗?或者,仅在直接派生自可能具有多个实例的类(即B、C、D、e和F;以及G和H(但仅用于基类e,而不用于基类D和F))的类上使用它就足够了吗?我个人的建议是从B和C开始:虚拟a,然后继续添加,直到编译器停止抱怨 实际上,我会说B和C:virtuala,G和H:virtuale,E:virtualb和C。所有其他继承链接都可以是普通继承。但是,这个怪物需要60年才能进行虚拟调用。从a、B、C和E类(位于菱形
virtual
?都是吗?或者,仅在直接派生自可能具有多个实例的类(即B、C、D、e和F;以及G和H(但仅用于基类e,而不用于基类D和F))的类上使用它就足够了吗?我个人的建议是从B和C开始:虚拟a,然后继续添加,直到编译器停止抱怨
实际上,我会说B和C:virtuala,G和H:virtuale,E:virtualb和C。所有其他继承链接都可以是普通继承。但是,这个怪物需要60年才能进行虚拟调用。从a、B、C和E类(位于菱形顶部)继承时,必须指定
virtual
继承
如果您希望每个类型的每个实例(只有一个A、只有一个B等)只有一个每个类型的“物理”实例,那么每次使用继承时只需使用虚拟继承即可
如果需要其中一种类型的单独实例,请使用普通继承 我一起玩了一个程序,可以帮助你研究虚拟基地的复杂性。它将
I
下的类层次结构打印为适合graphiviz()的有向图。每个实例都有一个计数器,可以帮助您理解构造顺序。以下是节目:
#include <stdio.h>
int counter=0;
#define CONN2(N,X,Y)\
int id; N() { id=counter++; }\
void conn() \
{\
printf("%s_%d->%s_%d\n",#N,this->id,#X,((X*)this)->id); \
printf("%s_%d->%s_%d\n",#N,this->id,#Y,((Y*)this)->id); \
X::conn(); \
Y::conn();\
}
#define CONN1(N,X)\
int id; N() { id=counter++; }\
void conn() \
{\
printf("%s_%d->%s_%d\n",#N,this->id,#X,((X*)this)->id); \
X::conn(); \
}
struct A { int id; A() { id=counter++; } void conn() {} };
struct B : A { CONN1(B,A) };
struct C : A { CONN1(C,A) };
struct D : B { CONN1(D,B) };
struct E : B,C { CONN2(E,B,C) };
struct F : C { CONN1(F,C) };
struct G : D,E { CONN2(G,D,E) };
struct H : E,F { CONN2(H,E,F) };
struct I : G,H { CONN2(I,G,H) };
int main()
{
printf("digraph inh {\n");
I i;
i.conn();
printf("}\n");
}
…结果为菱形(查看数字以了解构造顺序!!)
但如果你把所有的基地都虚拟化:
struct A { int id; A() { id=counter++; } void conn() {} };
struct B : virtual A { CONN1(B,A) };
struct C : virtual A { CONN1(C,A) };
struct D : virtual B { CONN1(D,B) };
struct E : virtual B, virtual C { CONN2(E,B,C) };
struct F : virtual C { CONN1(F,C) };
struct G : virtual D, virtual E { CONN2(G,D,E) };
struct H : virtual E, virtual F { CONN2(H,E,F) };
struct I : virtual G,virtual H { CONN2(I,G,H) };
您将得到一个具有不同初始化顺序的菱形:
玩得开心 如果您想确保层次结构中顶级类的对象(在您的例子中是
I
)包含每个父类的一个子对象,那么必须查找层次结构中具有多个超类的所有类,并使这些类成为其超类的虚拟基。就这样
在您的情况下,A
、B
、C
和E
类必须在每次您在此层次结构中从它们继承时成为虚拟基类
类
D
、F
、G
和H
不必成为虚拟基类。编辑的:我认为A是最派生的类;)
@路德的回答真的很酷,但回到原来的问题:
当从继承层次结构中至少有一个其他类继承的任何类继承时,您需要使用virtual
继承(在路德的图表中,它意味着至少有两个箭头指向该类)
在这里,在D
、F
、G
和H
之前没有必要这样做,因为只有一个类是从它们派生的(目前没有一个类是从I
派生的)
但是,如果您事先不知道另一个类是否会从基类继承,可以添加virtual
作为预防措施。例如,建议一个Exception
类从std::Exception
虚拟继承,而不是由Stroustrup自己继承
正如卢瑟所指出的,它修改了实例化顺序(并且对性能有轻微的影响),但我认为任何依赖于施工顺序的设计都是错误的。正如一个精确性:您仍然有保证基类在派生类的任何属性之前初始化,因此在派生的构造函数体执行之前。
< P>在C++中保持C++的继承表。添加的虚拟类越多,编译时间(链接)就越长,运行时也就越重一般来说,如果可以避免使用虚拟类,您可以使用一些模板进行替换,或者尝试以某种方式进行解耦。这是一个理论问题,还是您最终真的遇到了这样一种情况:它对您有真正的价值实际上,我们有这样的类层次结构(事实上,它们甚至更糟)。计划进行一些重构…这是一个相当不错的继承场景:)!!!!我认为他想去探索虚拟基地课程。虚拟继承调用序列你还必须
F:virtualc
和D:virtualb
,否则H将通过E虚拟继承C,而非虚拟继承F。你能解释为什么只有那些需要是虚拟的吗?为什么不是D:虚拟B和F:虚拟C?为什么在这样的类上虚拟函数调用会很慢呢?你是对的,那些可能也需要是虚拟的。您确实需要使用编译器检查它。至于slow,因为编译器需要为每个虚拟函数调用检查六种不同的类型。作为后续问题,您能否解释一下在任何地方使用虚拟继承会产生什么影响(如果有的话)?没有什么?或者我会为I的实例使用不同的(更大的)布局吗?在任何地方使用虚拟继承都会减慢速度,有点慢(尽管我从未测量过)。虚拟继承IMHO的主要缺点是,一旦您将对象强制转换为指向其虚拟基类的指针或引用,就无法将其强制转换回继承类。我不知道您不能向下转换指向虚拟基类的指针。谢谢你指出(没有双关语的意思)。呃,只是为了确保没有误解。您可以将D*
向下转换为a*
。但是,你不能向上投射
struct B : virtual A { CONN1(B,A) };
struct C : virtual A { CONN1(C,A) };
struct D : virtual B { CONN1(D,B) };
struct E : virtual B, virtual C { CONN2(E,B,C) };
struct F : virtual C { CONN1(F,C) };
struct G : D, virtual E { CONN2(G,D,E) };
struct H : virtual E,F { CONN2(H,E,F) };
struct I : G,H { CONN2(I,G,H) };
struct A { int id; A() { id=counter++; } void conn() {} };
struct B : virtual A { CONN1(B,A) };
struct C : virtual A { CONN1(C,A) };
struct D : virtual B { CONN1(D,B) };
struct E : virtual B, virtual C { CONN2(E,B,C) };
struct F : virtual C { CONN1(F,C) };
struct G : virtual D, virtual E { CONN2(G,D,E) };
struct H : virtual E, virtual F { CONN2(H,E,F) };
struct I : virtual G,virtual H { CONN2(I,G,H) };