2011-09-01 52 views
2

考虑以下上溯造型到基类:虚拟继承(钻石) - 做我为什么需要从最派生类

#include <iostream> 
#include <string> 
using namespace std; 


class A { 
public: 
    A(const char* sName) //conversion constructor 
      : _sName(sName) {cout<<"(1)"<<endl;} ; 
    A(const A& s) {cout<<"(2)"<<endl;} //copy constructor 
    virtual ~A() {cout<<"(3)"<<endl;} //destructor 
    void f1() {cout<<"(4)"<<endl; f2();} //Notice two commands! 
    virtual void f2() =0; 
    private: 
    string _sName; 
    }; 



    class B1: virtual public A { 
    public: 
    B1(const char* sAName, const char* sSName) 
      : _sName1(sAName), A(sSName) {cout<<"(5)"<<endl;} 
    B1(const B1& b1) : A(b1) {cout<<"(6)"<<endl;} 
    ~B1() {cout<<"(7)"<<endl;} 
    virtual void f1() {cout<<"(8)"<<endl;} 
    virtual void f2() {cout<<"(9)"<<endl; f3();} 
    virtual void f3() {cout<<"(10)"<<endl;} 
    private: 
    string _sName1; 
    }; 



    class B2: virtual public A { 
    public: 
    B2(const char* sAName, const char* sSName) 
      : _sName2(sAName), A(sSName) {cout<<"(11)"<<endl;} 
    B2(const B2& b2) : A(b2) {cout<<"(12)"<<endl;} 
    ~B2() {cout<<"(13)"<<endl;} 
    virtual void f3() {f1(); cout<<"(14)"<<endl;} 
    private: 
     string _sName2; 
    }; 

    class C: public B1, public B2 { 
    public: 
     C() : A(" this is A ") , B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {} 
     C (const C& c) : A(c) , B1(c) , B2(c) {} 
     ~C() {cout<<"(15)"<<endl;} 
     virtual void f1() {A::f1(); cout<<"(16)"<<endl;} 
     void f3() {cout<<"(17)"<<endl;} 
    }; 


    int main() { 
     /* some code */ 
     return 0; 
    } 

正如你所看到的,我在class C中加入的构造函数(构造函数的实现)。我不清楚的是,为什么我还需要从C到A的倒戈,如果B1在Ctor为我做这项工作? 意义,如果我写的C'S构造函数为:

C() : A(" this is A ") , B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {} 

我为什么不能写:

C() : B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {} 

感谢, 罗南

回答

2

虚拟基地的构造者被称为 nonvirtual ansestors ctor's。在你的例子中,B1 ctor不能调用C的虚拟基类构造函数,因为B1 ctor本身稍后会被调用。

+0

即使第二种格式可以工作,只要在抽象类A中存在缺省构造函数即可。缺少该缺省构造函数是第二种格式不能编译的真正原因,请参阅我的答案。 –

+0

@Als:我同意你的回答,但是没有任何矛盾。虚拟基础ctor被大多数派生类ctor调用。 Base Ctor可能是默认的或不是。在这种情况下,简单地忽略中间ctor中的虚拟基础调用 – user396672

1

因为 'B1' 和 'B2' 都使用与'C'相同的'A'记忆。如果你没有在'C'中指定'A'的构造,'B1'或'B2'中哪一个应该构造'A'?

你的术语在这里有点不对 - 这不是对'A'的倾倒。

+0

好的,那么如果没有upcast,那叫什么呢? –

+1

构造函数调用 – Pete

2

简而言之,因为它是标准要求的:你必须在最懒惰的类的ctor中初始化虚拟基础。

一个更详细的答案,这是因为你只有一个基础子对象用于虚拟基础,并且这个子对象可能在不同的基类中以不同的方式初始化。例如。在你的例子中

 

C() : B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {} 
 

你期望将什么值传递给Ctor,“B1”或“B2”?

1

因为您的class A没有默认构造函数。

编译器为每个类生成一个默认的构造函数,但是一旦你明确地重载了​​构造函数,它假定你想做一些特殊的事情,并且它不会生成默认的构造函数,并且当你的代码试图调用默认的构造函数时会导致错误。

下面的代码声明:

C() : B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {} 

导致调用类默认构造函数,这是根本不存在的,因此会导致错误。如果您为您的class A提供默认构造函数,那么compile fine也不会出现任何错误。

1

这里是一个快速尝试:

#include <iostream> 

class A { public: A(int v) : m_v(v) { std::cout << "Initializing A with " << v << std::endl; } int m_v; }; 
class B1 : public virtual A { public: B1(int v) : A(v) {} }; 
class B2 : public virtual A { public: B2(int v) : A(v) {} }; 
class C : public B1, public B2 { public: C(int v1, int v2, int v3) : A(v1), B1(v2), B2(v3) {} }; 

int main() 
{ 
    C c(1, 2, 3); 

    std::cout << "c.m_v: " << c.m_v << std::endl; 

    return EXIT_SUCCESS; 
} 

这个例子输出:

Initializing A with 1 
c.m_v: 1 

也就是说,似乎调用A::A()最派生类中是必需的,因为自从继承当实例化C时,B1B2的构造函数不会调用它。