文档库 最新最全的文档下载
当前位置:文档库 › 【免费下载】多重继承与虚继承

【免费下载】多重继承与虚继承

【免费下载】多重继承与虚继承
【免费下载】多重继承与虚继承

JAVA中的多层继承

继承的基本实现 类的继承格式 在JAVA中使用extends关键字完成类的继承关系,操作格式: ·class 父类{} //定义父类 ·class 子类extends 父类{} //使用extends关键字实现继承 继承:子类继承父类,可以扩展已有的功能. Extends关键字:称为扩展,子类扩展一个类,子类有时候也称为派生类。 继承的限制: 在JAVA中只允许单继承,不能使用多重继承,即:一个子类只能继承一个父类,但是允许进行多层继承,即:一个子类可以有一个父类,一个父类还可以有一个父类。 继承的关系表示图: 访问限制: 在使用继承的时候要注意:子类是不能直接访问父类中的私有成员的,但是子类可以调用父类中的非私有化方法,但是不能直接调用父类中的私有成员。

继承的进一步研究: 子类对象的实例化过程 在继承的操作中,对于子类对象的实例化也是有要求的:子类对象在实例化前必须首先调用父类中的构造方法之后再调用子类自己的构造方法: public class Test{ public static void main(String args[]){ Students st=new Students(); st.setName("zhangsan"); st.setAge(30); st.setSchool("sichuan agriculture university"); System.out.println("name is:"+st.getName()+",age is:"+st.getAge()+",school is:"+st.getSchool()); } } class Person{ private String name; private int age; public Person(){ System.out.println("super's constractor method"); } public void setName(String name){ https://www.wendangku.net/doc/0017329466.html,=name; } public void setAge(int age){ this.age=age; } public String getName(){ return name; } public int getAge(){ return age; }

Python 多重继承

Python 多重继承 继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能。 回忆一下4种动物: ?Dog - 狗狗; ?Bat - 蝙蝠; ?Parrot - 鹦鹉; ?Ostrich - 鸵鸟。 如果按照哺乳动物和鸟类归类,我们可以设计出这样的类的层次: 但是如果按照“能跑”和“能飞”来归类,我们就应该设计出这样的类的层次: 如果要把上面的两种分类都包含进来,我们就得设计更多的层次: ?哺乳类:能跑的哺乳类,能飞的哺乳类; ?鸟类:能跑的鸟类,能飞的鸟类。 这么一来,类的层次就复杂了: 如果要再增加“宠物类”和“非宠物类”,这么搞下去,类的数量会呈指数增长,很明显这样设计是不行的。 正确的做法是采用多重继承。首先,主要的类层次仍按照哺乳类和鸟类设计:

只需要先定义好 和 对于需要

对于需要 通过多重继承,一个子类就可以同时获得多个父类的所有功能。 MixIn 如果需要“混入”额外的功能, 这种设计通常称之为MixIn。 为了更好地看出继承关系,我们把 MixIn: MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。 Python自带的很多库也使用了MixIn。举个例子,Python自带了 造出合适的服务来。 比如,编写一个多进程模式的TCP服务,定义如下: 编写一个多线程模式的UDP服务,定义如下:

如果你打算搞一个更先进的协程模型,可以编写一个 这样一来,我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。

c++多重继承试题

(一)选择填空: (1) 下列对派生类的描述中,( )是错误的。 A. 一个派生类可以用作另一个派生类的基类。 B. 派生类至少应有一个基类。 C. 基类中成员被派生类继承后其映射的访问权限并无变化。 D. 派生类对象的栈区内存存储内容中除了本类部分的所有非静态数据成员外,还首先包含了它的基类部分的所有非静态数据成员。 (2) 主函数main可以访问派生类的对象中哪一类映射的基类成员?( ) A. 公有继承的基类部分的公有成员。 B. 公有继承的基类部分的保护成员。 C. 公有继承的基类部分的私有成员。 D. 保护继承的基类部分的公有成员。 (3) 多继承派生类建立对象时,( )被最先调用。 A. 派生类自己的构造函数。 B. 祖先虚基类的构造函数。 C. 祖先非虚的基类的构造函数。 D. 派生类中子对象的构造函数。 (4)下列关键字中,( )不是类定义中使用的关键字: A.class B.public C.switch D.private (二)判断下列描述的正确性,对者划√,错者划 ,错者划 者划 (1) C++语言中,允许单继承,也允许多继承。 (2) 派生类不能再派生出新的派生类。 (3) 在公有继承中,派生类对象的成员函数可以访问基类部分中的私有成员。 (4) 在公有继承中,派生类对象的成员函数不可以访问基类部分中的保护成员。 (5) 在私有继承中,派生类对象的成员函数不可以访问基类部分中的公有成员。 (6) 在保护继承中,派生类对象的成员函数可以访问基类部分中的保护成员。 (7) 用class定义的类中,缺省的访问权限是公有的。 s定义的类中,缺省的访问权限是公有的。 (1)义的类中,缺省的访问权限是公有的。 (三)写出以下程序运行结果 // exer_ch4_3.cpp #include class A { int a,b; public: A(int i, int j) {a=i; b=j;} void Move(int x, int y)

15(多重继承、虚继承的内存布局)

1.多重继承、虚继承的内存空间布局 对多重继承、虚继承的内存空间布局进行研究,循序渐进的进行处理,主要关注以下几点:1)偏移表 2)虚表 3)数据成员 4)它们的位置 5)它们的大小及内容 6)它们间的关系。 1.1 单继承,无虚函数的情况 单继承、无虚函数的情况是: 1)基类的数据成员 2)派生类新增的数据成员 派生类的大小是基类数据成员和派生类新增数据成员大小之和。 顺序是按照上面的基类、派生类的顺序进行布局。 1.2 单继承,有虚函数的情况 单继承、有虚函数的情况: 1)派生类的虚表指针 2)基类的数据成员 3)派生类新增的数据成员 其中,派生类的虚表,是在基类的虚表基础之上所作的修改,有可能是: 1)对基类中虚函数地址的覆盖 2)派生类中新增的虚函数地址 1)只要有虚函数,就有虚表产生。 2)虚表中条目的个数,是本类中虚函数的个数 3)虚表中各条目的顺序,与类中声明(定义)的虚函数顺序一致

多重继承、无虚函数的情况是: 1)基类的数据成员 2)基类的数据成员 3)派生类新增的数据成员 这里与1.1 单继承,无虚函数的情况的差别是——可能存在多个基类。 这里基类数据成员的排放,是按照继承的数据依次进行的。 1.4 多重继承,有虚函数的情况 多重继承,有虚函数的情况是: 1)基类的虚表指针 2)基类的数据成员 3)基类的虚表指针 4)基类的数据成员 5)派生类新增的数据成员 这里与1.2 单继承,有虚函数的情况的差别是——虚表 这里说基类的虚表指针,其实是不太恰当的,因为它们实际上是派生类虚表的一部分。也就说,派生类的虚表是由多个基类的虚表所构成的。不存在一个单一的派生类的虚表。派生类的虚表条目是在各基类的虚表基础之上修改所得,可能包括: 1)对基类中虚函数的覆盖,会更新各基类虚表中的条目 2)派生类中新增的虚函数地址,会追加到第一个继承的基类的虚表中 至此,上面 1.1 单继承,无虚函数的情况 1.2 单继承,有虚函数的情况 1.3 多重继承,无虚函数的情况 1.4 多重继承,有虚函数的情况 是从单继承/多重继承,无/有虚函数的角度进行的梳理。 下面将以菱形继承为主线,来进行梳理。 (菱形继承中可能出现二义性,会逐步的引入虚继承,虚基类的概念) 菱形继承(diamond-inheritance)

多重继承下的虚函数表

多重继承下的虚函数表 多态是C++语言中的一项重要的机制,虚函数就是为实现多态而设计的。多态就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”。而虚函数表在这种多态机制中起了核心调度的作用。由于是编译器在后台操作,所以它被蒙上了一层神秘的面纱。对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为VFTable。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,VFTable就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。在C++的标准中提到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置。这意味着我们通过对象实例的地址得到它的VFTable,然后就可以遍历其中函数指针,并调用相应的函数。 Code 1: class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; 我们可以通过Base的实例来得到虚函数表。 Code 2: typedef void(*Fun)(void); Base b; Fun pFun = NULL; cout << "虚函数表地址:" <<(int *)*(int*)(&b) << endl; cout << "虚函数表—第一个函数地址:" << (int*)*((int*)*(int*)(&b)) << endl;

不同继承方式下对基类成员的访问控制和使用虚基类解决多重继承的二义性

实验报告6 一、实验任务: 1、定义一个车(vehicl)基类,具有MaxSpeed、Weight等成员变量,run、 stop等成员函数,由此派生出自行车(bicycle)类、汽车(motorcar)类。 自行车(bicycle)类有高度(Height)等属性,汽车(motorcar)类有座 位数(SeatNum)等属性。从bicycle和motorcar派生出摩托车(motorcycle)类,在继承过程中,注意把vehicl设置为虚基类。否则会有什么问题? 二、实验步骤: 1、编写程序定义一个车(vehicl)基类,由此派生出自行车(bicycle)类、 汽车(motorcar)类,注意把vehicl设置为虚基类。再从bicycle和motorcar 派生出摩托车(motorcycle)类,在main中测试这个类。 2、编译成功后,把vehicl类设置为非虚基类,再编译一次,此时系统报错, 无法编译成功。因为若不把vehicl类设置为虚基类,会出现二义性错误,程序不能成功编译。 三、本次实验难点: 在不同继承方式下对基类成员的访问控制和使用虚基类解决多重继承的二义性问题为本次实验的重点和难点,要在实验前后着重阐述。 源程序: #include using namespace std; class vehicl { public: int v; int m; void run(){cout<<"程序开始运行"<

C++类的多重继承

前面讨论的是单继承,即一个类是从一个基类派生而来的。实际上,常常有这样的情况:一个派生类有两个或多个基类,派生类从两个或多个基类中继承所需的属性。C++为了适应这种情况,允许一个派生类同时继承多个基类,这种行为称为多重继承(multiple inheritance)。 声明多重继承的方法 如果已声明了类A、类B和类C,可以声明多重继承的派生类D: class D: public A, private B, protected C { 类D新增加的成员 } D是多重继承的派生类,它以公用继承方式继承A类,以私有继承方式继承B类,以保护继承方式继承C 类。D按不同的继承方式的规则继承A、B、C的属性,确定各基类的成员在派生类中的访问权限。 多重继承派生类的构造函数 多重继承派生类的构造函数形式与单继承时的构造函数形式基本相同,只是在初始表中包含多个基类构造函数。如 派生类构造函数名(总参数表列): 基类1构造函数(参数表列), 基类2构造函数(参数表列), 基类3构造函数(参数表列) { 派生类中新增数成员据成员初始化语句 } 各基类的排列顺序任意。 派生类构造函数的执行顺序同样为:先调用基类的构造函数,再执行派生类构造函数的函数体。调用基类构造函数的顺序是按照声明派生类时基类出现的顺序。 [例] 声明一个教师(Teacher)类和一个学生(Student)类,用多重继承的方式声明一个研究生(Graduate)派生类。教师类中包括数据成员name(姓名)、age(年龄)、title(职称)。学生类中包括数据成员name1(姓名)、age(性别)、score(成绩)。在定义派生类对象时给出初始化的数据,然后输出这些数据。

多重继承下的virtual table

1.1.1.多重继承下的虚成员函数 0001 class Base1 0002 { 0003 public : 0004 Base1(); 0005 virtual ~Base1(); 0006 virtual void speakClearly(); 0007 virtual Base1 *clone() const; 0008 protected : 0009 float data_Base1; 0010 }; 0011 class Base2 0012 { 0013 public : 0014 Base2(); 0015 virtual ~Base2(); 0016 virtual void mumble(); 0017 virtual Base2 *clone() const; 0018 protected : 0019 float data_Base2; 0020 }; 0021 class Derived : public Base1, public Base2 0022 { 0023 public : 0024 Dervied(); 0025 virtual ~Dervied(); 0026 virtual Dervied *clone() const; 0027 protected : 0028 float data_Dervied; 0029 }; Dervied支持虚函数(virtual function)的难度在于Base2 Subobject,有三个问题待解决:1)析构函数 2)Base2::mumble(); 3)一组clone函数 多重继承下,一个派生类产生n-1个额外的虚函数表,用于表示基类(上一层)的数目,单一继承也可以看作特殊的多重继承。对象模型如下所示:

继承与多态作业题

继承与多态习题 一.基本概念与基础知识自测题 8.1填空题 8.1.1 如果类α继承了类β,则类α称为(1)类,而类β称为(2)类。(3)类 的对象可作为(4)类的对象处理,反过来不行,因为(5)。如果强制转换则要注意(6)。 答案:(1)基类 (2)派生类 (3)派生类 (4)基类 (5)派生类有一些新成员 (6)只能派生类强制转换为基类 8.1.2 当用public继承从基类派生一个类时,基类的public成员成为派生类的(1)成员, protected成员成为派生类的(2)成员,对private成员是(3)。公有派生可以使其类的(4),所以公有派生是主流。 答案:(1)public成员 (2)protected成员 (3)不可访问 (4)接口不变 8.1.3 利用继承能够实现(1)。这种实现缩短了程序开发的时间,VC++中的(2)很 好地体现了这一点。 答案:(1)代码的复用 (2)MFC编程 8.1.4 一个派生类只有一个直接基类的情况称为(1),而有多个直接基类的情况称为 (2)。继承体现了类的(3)概念,这在MFC中得到了很好表现,MFC中只采用了(4)。 答案:(1)单继承 (2)多重继承 (3)层次 (4)单继承 8.1.5 C++中多态性包括两种多态性:(1)和(2)。前者是通过(3)实现的, 而后者是通过(4)和(5)来实现的。 答案:(1)编译时的 (2)运行时的 (3)函数和运算符的重载 (4)类继承关系 (5)虚函数 8.1.6 在基类中将一个成员函数说明成虚函数后,在其派生类中只要(1)、(2)和

(3)完全一样就认为是虚函数,而不必再加关键字(4)。如有任何不同,则认为是(5)而不是虚函数。除了非成员函数不能作为虚函数外,(6)、(7)和(8)也不能作为虚函数。 答案:(1)同虚函数名 (2)同参数表 (3)同返回类型。如基类中返回基类指针,而派生类中返回派生类指针是允许的 (4)virtual (5)重载 (6)静态成员函数 (7)内联函数 (8)构造函数 8.1.7 纯虚函数定义时在函数参数表后加(1),它表明程序员对函数(2),其本质 是将指向函数体的指针定为(3)。 答案:(1)=0 (2)不定义 (3)NULL 8.2简答题 8.2.1构造函数和析构函数可以继承吗?派生类构造函数各部分的执行次序是怎样的?答:构造函数和析构函数不可以继承。派生类构造函数各部分的执行次序是: 1.调用基类构造函数,按它们在派生类声明的先后顺序,依次调用。 2.调用新增成员对象的构造函数,按它们在类定义中声明的先后顺序,依次调用。 3.派生类的构造函数体中的操作。 8.2.2什么叫派生类的同名覆盖(override)? 答:如果派生类声明了一个和某个基类成员同名的新成员(当然如是成员函数,参数表也必须一样,否则是重载),派生类中的新成员就屏蔽了基类同名成员,类似函数中的局部变量屏蔽全局变量。称为同名覆盖(override)。 8.2.3派生类的析构函数中需完成什么任务?是否要编写对基数和成员对象的析构函数的 调用?为什么? 答:析构函数的功能是作善后工作,析构函数无返回类型也没有参数,情况比较简单。派生类析构函数定义格式与非派生类无任何差异,不要编写对基数和成员对象的析构函数的调用,只要在函数体内把派生类新增一般成员处理好就可以了,因为对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。 8.2.4为什么要使用虚基类?怎样定义虚基类?用一个实例来解释虚基类在其派生类中的 存储方式。 答:在多重继承是有可能出现同一基类的两个拷贝,为避免这种情况,可使用虚基类。虚基类(virtual base class)定义方式如下: class派生类名:virtual 访问限定符基类类名{...}; class派生类名:访问限定符virtual基类类名{...}; virtual 关键字只对紧随其后的基类名起作用。

(整理)多重继承的内存分布.

指针的比较 再以上面Bottom类继承关系为例讨论,下面这段代码会打印Equal吗? 1 Bottom* b = new Bottom(); 2 Right* r = b; 3 4 if(r == b) 5 printf("Equal!/n"); 先明确下这两个指针实际上是指向不同地址的,r指针实际上在b指针所指地址上偏移8字节,但是,这些C++内部细节不能告诉C++程序员,所以C++编译器在比较r和b时,会把r减去8字节,然后再来比较,所以打印出的值是"Equal". 多重继承 首先我们先来考虑一个很简单(non-virtual)的多重继承。看看下面这个C++类层次结构。 1 class Top 2 { 3 public: 4 int a; 5 }; 6 7 class Left : public Top 8 { 9 public: 10 int b; 11 }; 12 13 class Right : public Top 14 { 15 public: 16 int c; 17 }; 18 19 class Bottom : public Left, public Right 20 { 21 public: 22 int d; 23 }; 24 用UML表述如下:

注意到Top类实际上被继承了两次,(这种机制在Eif fel中被称作 repeated inheritance),这就意味着在一个bottom对象中实际上有两个a属性(attributes,可以通过bottom.Left::a和bottom.Right::a访问) 。 那么Left、Right、Bottom在内存中如何分布的呢?我们先来看看简单的Left和Right内存分布: [Right 类的布局和Left是一样的,因此我这里就没再画图了。刺猬] 注意到上面类各自的第一个属性都是继承自Top类,这就意味着下面两个赋值语句: 1 Left* left = new Left(); 2 Top* top = left; left和top实际上是指向两个相同的地址,我们可以把Left对象当作一个Top对象(同样也可以把Right对象当Top对象来使用)。但是Botom对象呢?GCC是这样处理的:

继承与组合

1.实验目的 (1)理解继承的含义,掌握派生类的定义方法和实现。 (2)理解公有继承下基类成员对派生类成员和派生类对象的可见性,能正确地访问继承层次中的各种类成员。 (3)掌握多重继承的概念、定义方法,熟悉多重继承派生类构造函数的执行顺序。 说明:多重继承派生类构造函数的执行顺序:①先执行所有基类的构造函数(顺序按照定义派生类时指定的各基关顺序),②再执行对象成员所在类的构造函数(顺序按照它们在类中的声明顺序) ,③最后执行派生类构造函数体中的内容。 2.实验内容 (1)继承访问权限 #include using namespace std; class Base { public: void setx(int i){ x=i;} int getx(){return x;} public: int x; }; class Derived: public Base { public: void sety(int i){ y=i; } int gety(){return y;} void show(){ cout<<”Base::x=”<

bb.sety(25); bb.show(); cout<<”Base::x=”< #include using namespace std; class Person { private: string m_strName; int m_nAge; public: Person(string name,int age) { m_strName=name; m_nAge = age; cout<<"constructor of person"<

相关文档