第九章 多重继承

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这个父类分别伴随B和C产生了两个拷贝。在调用拷贝中的方法时产生了矛盾。究竟是调用哪一个拷贝中的print()呢?于是,全部人都会想,要是仅仅有一个拷贝就好了,就没有矛盾了。虚拟继承就提供了这样的机制,按上面的样例,仅仅需改动B和C对A的继承方式,即加一个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;
}

重要结论:

基类构造函数假设有多个基类则构造函数的调用顺序是某类在类派生表中出现的

顺序而不是它们在成员初始化表中的顺序。

【ThinkingInC++】75、多重继承的更多相关文章

  1. c#继承、多重继承

    c#类 1.c#类的继承 在现有类(基类.父类)上建立新类(派生类.子类)的处理过程称为继承.派生类能自动获得基类的除了构造函数和析构函数以外的所有成员,可以在派生类中添加新的属性和方法扩展其功能.继 ...

  2. 编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议75~78)

    建议75:集合中的元素必须做到compareTo和equals同步 实现了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必须实现的,它与equals方法 ...

  3. J a v a 的“多重继承”

    接口只是比抽象类“更纯”的一种形式.它的用途并不止那些.由于接口根本没有具体的实施细节——也就是说,没有与存储空间与“接口”关联在一起——所以没有任何办法可以防止多个接口合并到一起.这一点是至关重要的 ...

  4. 75篇关于Tomcat源码和机制的文章

    75篇关于Tomcat源码和机制的文章 标签: tomcat源码机制 2016-12-30 16:00 10083人阅读 评论(1) 收藏 举报  分类: tomcat内核(82)  版权声明:本文为 ...

  5. java提高篇(九)-----实现多重继承

    多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.有些时候我们会认为如果系统中需要使用多重继承往往都是糟糕的设计,这个时候我们往往需 ...

  6. 不可或缺 Windows Native (22) - C++: 多重继承, 虚基类

    [源码下载] 不可或缺 Windows Native (22) - C++: 多重继承, 虚基类 作者:webabcd 介绍不可或缺 Windows Native 之 C++ 多重继承 虚基类 示例1 ...

  7. scala 学习笔记(06) OOP(下)多重继承 及 AOP

    一.多继承 上篇trait中,已经看到了其用法十分灵活,可以借此实现类似"多重继承"的效果,语法格式为: class/trait A extends B with C with D ...

  8. Effective C++ -----条款40:明智而审慎地使用多重继承

    多重继承比单一继承复杂.它可能导致新的歧义性,以及对virtual继承的需要. virtual继承会增加大小.速度.初始化(及赋值)复杂度等等成本.如果virtual base classes不带任何 ...

  9. python基础——多重继承

    python基础——多重继承 继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能. 回忆一下Animal类层次的设计,假设我们要实现以下4种动物: Dog - 狗狗: Bat ...

随机推荐

  1. 最好最实用的二次开发教程 cms

    ◆二次开发 什么是二次开发? 二次开发,简单的说就是在现有的软件上进行定制修改,功能的扩展,然后达到自己想要的功能和效果,一 般来说都不会改变原有系统的内核. 为什么要二次开发? 随着信息化技术的不断 ...

  2. 关于j使用ava匿名类的好处总结

    匿名类,除了只能使用一次,其实还有其他用处,比如你想使用一个类的protected方法时,但是又和这个类不在同一个包下,这个时候匿名类就派上用场了,你可以定义一个匿名类继承这个类,在这个匿名类中定义一 ...

  3. ubuntu下创建虚拟python3开发环境

    友情链接:ubuntu16.04下安装python3+创建虚拟python3开发环境 1.为什么要创建python3虚拟开发环境? /********************************* ...

  4. TensorFlow分布式部署【多机多卡】

    让TensorFlow们飞一会儿 前一篇文章说过了TensorFlow单机多卡情况下的分布式部署,毕竟,一台机器势单力薄,想叫兄弟们一起来算神经网络怎么办?我们这次来介绍一下多机多卡的分布式部署. 其 ...

  5. maven 编译打包时,明明类文件没有问题,却提示错误:未结束的字符串字面值,maven-compiler-plugin:2.3.2

    maven错误提示如下: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (de ...

  6. 查找并修复Android中的内存泄露—OutOfMemoryError

    [编者按]本文作者为来自南非约翰内斯堡的女程序员 Rebecca Franks,Rebecca 热衷于安卓开发,拥有4年安卓应用开发经验.有点完美主义者,喜爱美食. 本文系国内ITOM管理平台 One ...

  7. Eigen学习之Array类

    Eigen 不仅提供了Matrix和Vector结构,还提供了Array结构.区别如下,Matrix和Vector就是线性代数中定义的矩阵和向量,所有的数学运算都和数学上一致.但是存在一个问题是数学上 ...

  8. npm用法及离线安装方法

    npm用法及离线安装方法 基本的用法 查看某个模块的全部信息,或者可以查看单个信息 npm info name npm info name version npm info name homepage ...

  9. 借助System.Linq.Dynamic, IQueryable根据排序字符串排序

    在使用Entity Framework时,若有多个排序,需要OrderBy (OrderByDescending)再ThenBy (ThenByDescending) 假设需要根据Name升序排序,再 ...

  10. 使用NSOperation以及NSOperationQueue

    使用NSOperation以及NSOperationQueue NSOperation vs. Grand Central Dispatch (GCD) 在Mac OS X v10.6和iOS4之前, ...