【ThinkingInC++】75、多重继承

第九章 多重继承

9.2 接口继承

Intertfacees.cpp

/**

* 书本:【ThinkingInC++】

* 功能:接口继承Interfaces.cpp

* 时间:2014年10月28日20:06:31

* 作者:cutter_point

*/

 

#include <iostream>

#include <sstream>

#include <string>

 

using namespace std;

 

class Printable //抽象类

{

public:

   virtual ~Printable() {} //虚函数

   virtual void print(ostream&) const=0;   //纯虚函数

};

 

class Intable

{

public:

   virtual ~Intable() {}

   virtual int toInt() const=0;

};

 

class Stringable

{

public:

   virtual ~Stringable() {}

   virtual string toString() const=0;

};

 

class Able : public Printable, publicIntable, public Stringable

{

   int myData;

public:

   Able(int x) { myData=x; }

   void print(ostream& os) const { os<<myData; }

   int toInt() const { return myData; }

   string toString() const

    {

       ostringstream os;

       os<<myData;

       return os.str();

    }

};

 

void testPrintable(const Printable& p)

{

   p.print(cout);

   cout<<endl;

}

 

void testIntable(const Intable& n)

{

   cout<<n.toInt()+1<<endl;

}

 

void testStringable(const Stringable&s)

{

   cout<<s.toString()+"th"<<endl;

}

 

int main()

{

   Able a(7);

   testPrintable(a);

   testIntable(a);

   testStringable(a);

 

   return 0;

}


9.5 虚基类

虚拟继承的机制

实际上造成上边的二义性的根本原因是在这样的继承的特殊模式下。A这个父类分别伴随BC产生了两个拷贝。在调用拷贝中的方法时产生了矛盾。究竟是调用哪一个拷贝中的print()呢?于是,全部人都会想,要是仅仅有一个拷贝就好了,就没有矛盾了。虚拟继承就提供了这样的机制,按上面的样例,仅仅需改动BCA的继承方式,即加一个keyword virtual

class B:  virtual public A

{

    public:

        B(){cout << "B called"<< endl;}

       

    private:

           

};

class C:  virtual public A

{

    public:

        C(){cout << "C called"<< endl;}

           private:

           

};


这样就相当于说。在没有A类的拷贝时就构造一个,假设已经有了,就用已经有的那一个,这样一来,拷贝仅仅有一份了。二义性消除了。

static_cast 和 dynamic_cast

http://www.cnblogs.com/chio/archive/2007/07/18/822389.html

dynamic_cast主要用于类层次间的上行转换和下行转换。还能够用于类之间的交叉转换。



在类层次间进行上行转换时。dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

子对象的初始化顺序遵循例如以下规则:

1)  全部虚基类子对象,依照他们在类定义中出现的位置,重上到下、从左到右初始化

2)  然后非虚基类按通常顺序初始化

3)  全部的成员对象按声明的顺序初始化

4)  完整的对象的构造函数运行

前面讲过,为了初始化基类的子对象。派生类的构造函数要调用基类的构造函数。

对于虚基类来讲,因为派生类的对象中仅仅有一个虚基类子对象。为保证虚基类子对象仅仅被初始化一次,这个虚基类构造函数必须仅仅被调用一次。

http://blog.csdn.net/maojudong/article/details/8169240

假设类继承中包含多个虚基类的实例。基类仅仅被初始化一次。

1、假设类里面有成员类,成员类的构造函数优先被调用。

2、创建派生类的对象。基类的构造函数函数优先被调用(也优先于派生类里的成员类);

3、 基类构造函数假设有多个基类则构造函数的调用顺序是某类在类派生表中出现的
顺序而不是它们在成员初始化表中的顺序。


4、成员类对象构造函数假设有多个成员类对象则构造函数的调用顺序是对象在类中
被声明的顺序而不是它们出如今成员初始化表中的顺序;


5、派生类构造函数
作为一般规则派生类构造函数应该不能直接向一个基类数据成员赋值而是把值传递
给适当的基类构造函数否则两个类的实现变成紧耦合的(tightly coupled)将更加难于
正确地改动或扩展基类的实现。

(基类设计者的责任是提供一组适当的基类构造函数)

VirtInit.cpp

//关于虚基类的初始化问题

/**
* 书本:【ThinkingInC++】
* 功能:关于虚基类的初始化问题
* 时间:2014年10月28日20:07:25
* 作者:cutter_point
*/

#include <iostream>
#include <string>

using namespace std;

class M
{
public:
    M(const string& s) { cout<<"M "<<s<<endl; } //每个类都有嵌入的M类型成员
};

class A
{
    M m;    //这里是一个类的组合
public:
    A(const string& s) : m("in A")
    {
        cout<<"A "<<s<<endl;    //跟踪类A的初始化
    }
    virtual ~A() { cout<<"析构A"<<endl; } //这是一个虚基类
};

class B
{
    M m;    //这里是一个类的组合
public:
    B(const string& s) : m("in B")
    {
        cout<<"B "<<s<<endl;    //跟踪类A的初始化
    }
    virtual ~B() { cout<<"析构B"<<endl; } //这是一个虚基类
};

class C
{
    M m;    //这里是一个类的组合
public:
    C(const string& s) : m("in C")
    {
        cout<<"C "<<s<<endl;    //跟踪类A的初始化
    }
    virtual ~C() { cout<<"析构C"<<endl; } //这是一个虚基类
};


class D
{
    M m;    //这里是一个类的组合
public:
    D(const string& s) : m("in D")
    {
        cout<<"D "<<s<<endl;    //跟踪类A的初始化
    }
    virtual ~D() { cout<<"析构D"<<endl; } //这是一个虚基类
};

class F : virtual public B, virtual public C, public D  //虚继承
{
    M m;
public:
    F(const string& s) : B("from F"), C("from F"), D("from F"), m("in F")
    {
        cout<<"F "<<s<<endl;
    }
};

//開始多重继承
class E : public A, virtual public B, virtual public C  //虚继承
{
    M m;
public:
    E(const string& s) : A("from E"), B("from E"), C("from E"), m("in E")
    {
        cout<<"E "<<s<<endl;
    }
};



//终于的继承E,F
class G : public E, public F
{
    M m;
public:
    //这里初始化的顺序和继承的顺序不同。看看结果,结果是按继承的顺序初始化
    G(const string& s) : B("from G"), C("from G"), F("from G"), E("from G"), m("in G")
    {
        cout<<"G "<<s<<endl;
    }
};

int main()
{
    //构造函数的调用顺序是某类在类派生表中出现的顺序

    G g("from main");

    return 0;
}


重要结论:

基类构造函数假设有多个基类则构造函数的调用顺序是某类在类派生表中出现的
顺序而不是它们在成员初始化表中的顺序。

原文地址:https://www.cnblogs.com/mengfanrong/p/5367602.html