第九章 多重继承

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. CSS属性之border

    css的border属性相信大家都不陌生了,就是给元素加个边框嘛,在不同的盒模型下,会给元素的宽高带来怎样的影响,相信大家也都很熟悉了,这里就不再赘述,只说说大家平时没有怎么留意的东西. 0.bord ...

  2. 献给java求职路上的你们

    为了更好的树立知识体系,我附加了相关的思维导图,分为pdf版和mindnote版.比如java相关的导图如下: 由于时间仓促,有些地方未写完,后面会继续补充.如有不妥之处,欢迎及时与我沟通. 相关概念 ...

  3. C# Base64Helper

    public static class Base64Helper { /// <summary> /// base64字符保存图片到本 /// </summary> /// & ...

  4. Linux 启动脚本及chkconfig命令之自启动服务

    有时我们会碰到这样的情况,系统启动的时候报一大堆无法连接mysql的错误,问题在mysql数据库还没有启动的时候已经启动了一些需要连接mysql数据库的服务.这样我们就得修改启动顺序,把需要连接mys ...

  5. maven 插件jetty/tomcat启动 web 应用

    tomcat <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomca ...

  6. java 标准输出流、标准错误输出流、标准输入流及扫描仪

    初步认识标准输出流.错误输出流.输入流.扫描仪 package com.mydemo.controller; import java.util.Scanner; public class HelloW ...

  7. 使用UICollectionView

    使用UICollectionView 使用UICollectionView的流程: 1. 设定一个UICollectionViewFlowLayout 2. 使用这个设定的UICollectionVi ...

  8. LintCode,hihoCoder,LeetCode有什么区别?

    https://www.zhihu.com/question/31218682 知乎用户 9 人赞同了该回答 LintCode 和LeetCode的题差不太多LintCode 有中文,不过没有用户讨论 ...

  9. 铁乐学python_day21_面向对象编程3

    抽象类和接口类 以下内容大部分摘自博客http://www.cnblogs.com/Eva-J/ 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某 ...

  10. n=n+1 放在print(s)的上面的影响 (2) n=n=+1在前面,则不满足前面<100条件时候,才跳出while的循环,这时候while循环结束, 到了外面的下一步-->print()

    1+2+3+....+100=     ? n=1 s = 0 while n < =100: s = s+n n= n+1 # n=n+1    在print(s)上面的情况 print(s)