关于虚拟继承的程序输出的困惑

时间:2018-03-26 15:03:28

标签: c++ language-lawyer multiple-inheritance virtual-inheritance name-lookup

我是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 也让我感到困惑。有人可以解释一下这里发生了什么吗?我觉得我理解虚拟继承的原理。

3 个答案:

答案 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::mD作为间接基类后,会将其替换为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)

虚拟类“冒泡”到层次结构的顶部,但在查找期间在非虚拟类之后考虑它们。

示例1

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

示例2

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