我是c ++的新手,并且一直在尝试虚拟继承。但有些事情让我感到困惑。
ui=fluidPage(
fluidRow(
column(width = 12,
numericInput("choice", "Highlight in red when carb=", 1,),
absolutePanel(plotOutput("plot1"), top = 200, left = 0,width = 500, height = 500),
absolutePanel(plotOutput("plot2"), top = 200, left = 0,width = 500, height = 500)
)
)
)
server=function(input, output) {
p=reactive({return(ggplot(data=data_,aes(x=wt,y=mpg))+geom_blank()+facet_wrap(~carb)+
geom_point(data=data_ %>% filter(carb==input$choice),color='red',size=3)+
theme_bw()+
theme(legend.position="none")+
theme(
panel.background =element_rect(fill = "transparent"),
plot.background = element_rect(fill = "transparent"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
)
)})
output$plot1=renderPlot({p_0},bg="transparent")
output$plot2=renderPlot({p()},bg="transparent")
}
shinyApp(ui, server)
这个输出是:
#include <iostream>
using namespace std;
struct A {int m = 5005;};
struct B : A {};
struct C : virtual B {};
struct D : virtual B {int m = 6006;};
struct E : C, D {};
int main () {
E e;
e.m = 303;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
}
现在,令我困惑的是e.A::m = 5005
e.D::m = 303
e.m = 303
。不应该是6006吗?我知道虚拟继承已经存在很多问题,但没有人真正解释为什么会这样。
我也认为我发现了另一个显示相同“问题”的程序。
e.D::m = 303
输出
#include <iostream>
using namespace std;
struct S {int m = 101;};
struct A : virtual S {int m = 202;};
struct B : virtual S {int m = 303;};
struct C : virtual A, virtual B {int m = 404;};
struct D : C {};
struct E : virtual A, virtual B, D {};
int main () {
E e;
e.m = 10;
cout << "e.S::m = " << e.S::m << endl;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.B::m = " << e.B::m << endl;
cout << "e.C::m = " << e.C::m << endl;
cout << "e.m = " << e.m << endl;
}
此处e.S::m = 101
e.A::m = 202
e.B::m = 303
e.C::m = 10
e.m = 10
也让我感到困惑。有人可以解释一下这里发生了什么吗?我觉得我理解虚拟继承的原理。
答案 0 :(得分:2)
我认为段落here解释了这种行为:
无论哪种方式,在检查派生类的基础时,遵循以下规则,有时称为虚拟继承中的支配性:
构造查找集,其中包含声明和 找到这些声明的子对象。 使用声明由它们表示的成员和类型替换 声明,包括inject-class-names,由类型替换 他们代表。如果C是使用其名称范围的类,则C 首先检查。如果C中的声明列表为空,则查找 set是为它的每个直接碱基Bi构建的(递归应用 如果Bi有自己的基础,这些规则)。一旦构建,查找设置为 直接基数合并到C中的查找集中,如下所示
- 如果Bi中的声明集为空,则将其丢弃
- 如果到目前为止构建的C的查找集为空,则将其替换为Bi
的查找集- 如果Bi的查找集中的每个子对象都是至少一个的基础 子对象已经添加到C的查找集中,查找集为 Bi被丢弃了。
- 如果每个子对象已经添加到查找集中 C是Bi的查找集中的至少一个子对象的基础,然后 C的查找集被丢弃并替换为查找集 毕
- 否则,如果Bi和C中的声明集不同, 结果是一个模糊的合并:C的新查找集有一个 无效声明和子对象的并集ealier合并到C中 并从Bi介绍。如果这个无效的查找集可能不是错误 它会在以后丢弃。
- 否则,C的新查找集具有 共享声明集和ealier合并的子对象的并集 进入C并从Bi
引入
该示例有助于说明逻辑:
struct X { void f(); };
struct B1: virtual X { void f(); };
struct B2: virtual X {};
struct D : B1, B2 {
void foo() {
X::f(); // OK, calls X::f (qualified lookup)
f(); // OK, calls B1::f (unqualified lookup)
// C++11 rules: lookup set for f in D finds nothing, proceeds to bases
// lookup set for f in B1 finds B1::f, and is completed
// merge replaces the empty set, now lookup set for f in C has B1::f in B1
// lookup set for f in B2 finds nothing, proceeds to bases
// lookup for f in X finds X::f
// merge replaces the empty set, now lookup set for f in B2 has X::f in X
// merge into C finds that every subobject (X) in the lookup set in B2 is a base
// of every subobject (B1) already merged, so the B2 set is discareded
// C is left with just B1::f found in B1
// (if struct D : B2, B1 was used, then the last merge would *replace* C's
// so far merged X::f in X because every subobject already added to C (that is X)
// would be a base of at least one subobject in the new set (B1), the end
// result would be the same: lookup set in C holds just B1::f found in B1)
}
};
TL; DR:因为e.m = 303;
是非限定查找,编译器将以递归方式查找继承树以匹配声明。在这种情况下,我认为首先会找到A::m
,但在看到D::m
将D
作为间接基类后,会将其替换为A
。因此e.m
最终会解析为e.D::m
。
答案 1 :(得分:0)
我正在回答你的第一个问题。你刚刚在你的D类中写了m的值。
E e;
e.m = 303;//you just over write the value 6006, just comment out this line and check
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
这里B继承了A和B几乎已经继承到C和D,所以最后只有E的一个副本可用。因此,您可以访问A类到A :: m的数据成员m
,并且您的e.m和e.D :: m正在访问相同的数据成员,即D类的m
。
现在看下面一些有趣的结果: -
//e.m = 303;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
e.m = 303;//over write D::m;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
e.E::m = 101;//over write D::m
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
e.B::m = 202;//over write A::m through B
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
答案 2 :(得分:0)
虚拟类“冒泡”到层次结构的顶部,但在查找期间在非虚拟类之后考虑它们。
struct E : C, D {};
成为(圆括号中的虚拟类):
E
C
D
(B) - bubbled up to E from other classes.
接下来我们来看看C和D中的内容:
E
C <-- continue lookup
(B) <-- will not continue to look for m here yet
D
(B) <-- will not continue to look for m here yet either
m <-- m found during unqualified lookup
(B) <-- may continue to look for m here, but already found it above
struct E : virtual A, virtual B, D {};
变为:
E
D - first non-virtual class at top level
(A)
(B)
(S) - bubbled up from other classes.
接下来我们来看看D中的内容:
E
D <-- continue lookup
C <-- continue lookup
m <-- m found during unqualified lookup
(A) <-- no further lookup, m already found
(B) <-- no further lookup, m already found
(S) <-- no further lookup, m already found