文档库 最新最全的文档下载
当前位置:文档库 › 4.6.多重继承与虚基类

4.6.多重继承与虚基类

多重继承与虚基类

【ITjob课程资料】

多重继承与虚基类

如果一个派生类同时有两个或者多个基类,派生类从两个和多个基类中继承所需的属性,这种继承方式称为多重继承(Multiple Inheritance)。如果一个派生类有多个直接基类,而这些直接基类又有一个共同的成员,则在生成派生类对象时,系统会为派生类对象生成共同基类成员变量的多份拷贝。如果希望派生类对象中只包含一份共同基类的成员变量,则可以在声明派生类时,通过virtual继承方式,使派生对象只保留共同基类的一份成员变量。

例4.8 没有同名成员的多重继承

#include

using namespace std;

class CBaseA

{

public:

int m_ai;

void AFunc()

{

m_ai++;

cout<

}

};

class CBaseB

{

public:

int m_bi;

void BFunc()

{

m_bi++;

cout<

}

};

class CDerived:public CBaseA,public CBaseB

{

public:

int m_j;

};

int main()

{

CDerived d;

d.m_ai=1;

d.AFunc();

d.m_bi=2;

d.BFunc();

return 0;

}

程序运行结果如下:

2

3

Press any key to continue

说明:

在例4.8中,CDerived公有继承了CBaseA、CBaseB,在两个基类中没有同名的成员。例4.9 具有同名成员的多重继承

#include

using namespace std;

class CBaseA

{

public:

int m_i;

void Func()

{

m_i++;

cout<

}

};

class CBaseB

{

public:

int m_i;

void Func()

{

m_i++;

cout<

}

};

class CDerived:public CBaseA,public CBaseB

{

public:

int m_j;

};

int main()

{

CDerived d;

d.m_i=1;

d.Func();

return 0;

}

程序运行结果如下:

error C2385: 'CDerived::m_i' is ambiguous

warning C4385: could be the 'm_i' in base 'CBaseA' of class 'CDerived'

warning C4385: or the 'm_i' in base 'CBaseB' of class 'CDerived'

error C2385: 'CDerived::Func' is ambiguous

warning C4385: could be the 'Func' in base 'CBaseA' of class 'CDerived'

warning C4385: or the 'Func' in base 'CBaseB' of class 'CDerived'

说明:

在例4.9中,CDerived公有继承了CBaseA、CBaseB,在两个基类中有同名的成员,成员变量m_i和成员函数Func()。执行语句CDerived d;后,对象d的内存结构如图4.2所示,它继承了两个基类的的变量m_i;两个基类的变量m_i都在对象d中都有一份拷贝,所以执行语句d.m_i=1;时,编译器不知道调用从谁继承过来的m_i,所以就产生的二义性的问题。即不知道给那个m_i赋值。编译时报错CDerived::m_i' is ambiguous。同理执行语句d.Func();也会发生二义性。编译时报错,'CDerived::Func' is ambiguous。

图4.2 对象d内存结构图

要改正上述的错误,我们可以加上作用域限定符,即 d. CBaseA::m_i,或者 d. CBaseB::m_i。这样在赋值或者引用时编译器就能区分那个m_i了。把程序的main()部分修改如下:

int main()

{

CDerived d;

d.CBaseA::m_i=10;

d.CBaseA::Func();

d.CBaseB::m_i=1;

d.CBaseB::Func();

return 0;

}

运行结果如下:

11

2

Press any key to continue

这里我们就很好地解决了二义性的问题。但是类CDerived的实例中就有多个m_i的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。

例4.10 虚基类

#include

using namespace std;

class CBase

{

public:

int m_i;

void Func()

{

m_i++;

cout<

}

};

class CBaseA:virtual public CBase//虚基类

{

public:

int m_a;

};

class CBaseB:virtual public CBase

{

public:

int m_b;

};

class CDerived: public CBaseA,public CBaseB

{

public:

int m_j;

};

int main()

{

CDerived d;

d.m_i=10;

d.Func();

return 0;

}

程序运行结果如下:

11

Press any key to continue

说明:在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如m_i这个成员,从类CDerived这个角度上来看,它是从类CBaseA与类CBaseB继承过来的,而类CBaseA 、CBaseB又是从类CBase继承过来的,但它们只保留一个副本,即在CDerived的实例里只保存了m_i的一个副本。这样就有效的防止了二义性,因此在主函数中调用d.m_i时就不会产生错误。

总结虚基类:

1)一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。

2)在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。

3)虚基类子对象是由派生类的构造函数通过调用虚基类的构造函数进行初始化的。

4)派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。

5)从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。

6)在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

相关资料与视频请访问ITjob就业培训网

C 第八章继承与派生习题习题解答

第八章继承与多态习题 一.基本概念与基础知识自测题 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.4 一个派生类只有一个直接基类的情况称为(1),而有多个直接基类的情况称为 (2)。继承体现了类的(3)概念,这在MFC中得到了很好表现,MFC中只采用了(4)。 答案:(1)单继承 (2)多重继承 (3)层次 (4)单继承 8.1.6 C++中多态性包括两种多态性:(1)和(2)。前者是通过(3)实现的, 而后者是通过(4)和(5)来实现的。 答案:(1)编译时的 (2)运行时的 (3)函数和运算符的重载 (4)类继承关系 (5)虚函数 8.2简答题(以下习题题号可能和教材不一致!) 8.2.1构造函数和析构函数可以继承吗?派生类构造函数各部分的执行次序是怎样的?答:构造函数和析构函数不可以继承。派生类构造函数各部分的执行次序是: 1.调用基类构造函数,按它们在派生类声明的先后顺序,依次调用。 2.调用新增成员对象的构造函数,按它们在类定义中声明的先后顺序,依次调用。 3.派生类的构造函数体中的操作。 8.2.2什么叫派生类的同名覆盖(override)?

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/df2476987.html,=name; } public void setAge(int age){ this.age=age; } public String getName(){ return name; } public int getAge(){ return age; }

C++基类和派生类

本讲讨论基类和派生类的基本概念。 通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。 在C++(C++培训)++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从 多个基类派生的继承称为多继承。 派生类的定义格式 单继承的定义格式如下: class <派生类名>:<继承方式><基类名> { <派生类新定义成员> }; 其中,<派生类名>是新定义的一个类的名字,它是从<基类名>中派生的,并且按指定的<继承方式>派生的。<继承方式>常使用如 下三种关键字给予表示: public 表示公有基类; private 表示私有基类; protected 表示保护基类; 多继承的定义格式如下:

class <派生类名>:<继承方式1><基类名1>,<继承方式2><基 类名2>,… { <派生类新定义成员> }; 可见,多继承与单继承的区别从定义格式上看,主要是多继承的 基类多于一个。 派生类的三种继承方式 公有继承(public)、私有继承(private)、保护继承(protected)是常 用的三种继承方式。 1. 公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。 2. 私有继承(private) 私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。 3. 保护继承(protected) 保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的 私有成员仍然是私有的。 下面列出三种不同的继承方式的基类特性和派生类特性。 不同继承方式的基类和派生类特性

Python 多重继承

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

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

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

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

C++继承与派生类习题

第九章继承与派生类 9.2 典型例题分析与解答 例题1:下列对派生类的描述中,()是错误的。 A.一个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类成员 D.派生类中继承的基类成员的访问权限到派生类保持不变 答案:D 分析:一个派生类可以作为另一个派生类的基类。无论是单继承还是多继承,派生类至少有一个基类。派生类的成员除了它自己的成员外,还包含了它的基类成员。派生类中继承的基类成员的访问权限到派生类受继承方式影响的,对于私有继承,基类的public,protected成员在派生类中作为private成员;对于公有继承,基类的public,protected成员在派生类中访问属性不变;对于保护继承,基类的public、protected成员在派生类中作为protected成员。例题2:派生类的对象对它的哪一类基类成员是可以访问的?() A.公有继承的基类的公有成员 B. 公有继承的基类的保护成员 C. 公有继承的基类的私有成员 D. 保护继承的基类的公有成员 答案:A 分析:公有继承的基类的公有成员在派生类中保持公有访问权限,所以派生类对象可以访问它;公有继承的基类的保护成员在派生类中保持保护访问权限,所以派生类对象不可以访问它;基类的私有成员不能被派生到派生类中,所以派生类对象不可以访问它;保护继承的基类的公有成员在派生类中变成保护的访问权限,所以派生类对象不可以访问它。 例题3:关于多继承二义性的描述,()是错误的。 A.派生类的多个基类中存在同名成员时,派生类对这个成员访问可能出现二义性 B.一个派生类是从具有共同的间接基类的两个基类派生来的,派生类对该公共基类的访问可能出现二义性 C.解决二义性最常用的方法是作用域运算符对成员进行限定 D.派生类和它的基类中出现同名函数时,将可能出现二义性 答案:D 分析:出现二义性有两种情况:调用不同基类的相同成员时可能出现二义性;访问共同基类的成员时可能出现二义性。消除二义性的方法是采用作用域运算符。派生类和它的基类中出现同名函数时,不可能出现二义性。 例题4:多继承派生类构造函数构造对象时,()被最先调用。 A.派生类自己的构造函数 B.虚基类的构造函数 C.非虚基类的构造函数 D.派生类中子对象类的构造函数 答案:B 分析:多继承派生类构造函数构造对象时,构造函数的调顺序是:虚基类的构造函数,派生类中子对象类的构造函数,派生类自己的构造函数。 例题5: C++类体系中,能被派生类继承的是()。 A.构造函数 B.虚函数 C.析构函数 D.友元函数

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)

C继承与派生类习题

第九章继承与派生类 9. 2 典型例题分析与解答 例题1:下列对派生类的描述中,()是错误的。 A—个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类成员 D.派生类中继承的基类成员的访问权限到派生类保持不变 答案:D 分析:一个派生类可以作为另一个派生类的基类。无论是单继承还是多继承,派生类至少有一个基类。派生类的成员除了它自己的成员外,还包含了它的基类成员。派生类中继承的基类成员的访问权限到派生类受继承方式影响的,对于私有继承,基类的 public ,protected 成员在派生类中作为private 成员;对于公有继承,基类的public ,protected 成员在派生类中访问属性不变;对于保护继承,基类的 public 、protected 成员在派生类中作为protected 成员。 例题2:派生类的对象对它的哪一类基类成员是可以访问的() A.公有继承的基类的公有成员 B.公有继承的基类的保护成员 C. 公有继承的基类的私有成员 D. 保护继承的基类的公有成员 答案: A 分析:公有继承的基类的公有成员在派生类中保持公有访问权限,所以派生类对象可以访问它;公有继承的基类的保护成员在派生类中保持保护访问权限,所以派生类对象不

可以访问它;基类的私有成员不能被派生到派生类中,所以派生类

对象不可以访问它;保护继承的基类的公有成员在派生类中变成保护的访问权限,所以派生类对象不可以访问它。 例题3:关于多继承二义性的描述,()是错误的。 A.派生类的多个基类中存在同名成员时,派生类对这个成员访问可能出现二 义性 B.—个派生类是从具有共同的间接基类的两个基类派生来的,派生类对该公 共基类的访问可能出现二义性 C.解决二义性最常用的方法是作用域运算符对成员进行限定 D.派生类和它的基类中出现同名函数时,将可能出现二义性 答案:D 分析:出现二义性有两种情况:调用不同基类的相同成员时可能出现二义性;访 问共同基类的成员时可能出现二义性。消除二义性的方法是采用作用域运算符。 派生类和它的基类中出现同名函数时,不可能出现二义性。 例题4:多继承派生类构造函数构造对象时,()被最先调用。 A.派生类自己的构造函数 B.虚基类的构造函数 C.非虚基类的构造函数 D.派生类中子对象类的构造函数 答案:E 分析:多继承派生类构造函数构造对象时,构造函数的调顺序是:虚基类的构造函数,派生类中子对象类的构造函数, 派生类自己的构造函数。 例题5: C++类体系中,能被派生类继承的是()。 A.构造函数 B.虚函数 C.析构函数 D.友元函数 答案:E 分析:C++类体系中,构造函数、析构函数和友元函数是不能被派生类继承的. 例题6:设有

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<<"程序开始运行"<

继承与派生类知识要点

继承与派生类 知识要点 1.1.掌握继承和派生的定义,派生类的定义方法。 (1)掌握继承的两种类型:单继承和多继承。 (2)掌握private,public,protected三种继承方式的特点。继承方式决定了基类中的成员在派生类中的属性。三种继承方式的共同点:基类的private成员在派生类中不可见。 区别:对于私有继承,基类的public、protected成员在派生类中作为private成员;对于公有继承,基类的public、protected成员在派生类中访问属性不变;对于保护继承,基类的public、protected成员在派生类中作为protected成员。 (3)掌握派生类中的构造函数和析构函数的使用。基类的构造函数和析构函数不能继承,所以必要时在派生类中定义自己的构造函数和析构函数。派生列的构造函数完成基类中新增数据成员和基类数据成员的初始化,基类数据成员的初始化通过基类构造函数来实现。 (4)掌握派生类的同名覆盖规则。 (5)掌握赋值兼容规则。基类对象可以使用公有派生类对象来代替,包括:派生类对象可以赋值给基类对象;派生类对象可以初始化基类对象的引用;基类类型指针可以指向派生类对象。 2.2.掌握多重继承的概念、定义方法、多重继承派生类构造函数的执行顺序。派生类构造函数的执行顺序是先执行所有基类的构造函数(顺序按照定义派生类时指定 的各基类顺序),在执行对象成员所在类的构造函数(顺序按照他们在类中的声明 顺序),最后执行派生类构造函数体中的内容。 3.3.掌握虚基类的概念和定义方法。在多重继承中,如果多条继承路径上有一个公共的基类,则在这些路径的汇合点上的派生类会产生来自不同路径的公共基类的多 个拷贝,如果用virtual把公共基类定义成虚基类,则只会保留公共基类的一个拷贝。

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个额外的虚函数表,用于表示基类(上一层)的数目,单一继承也可以看作特殊的多重继承。对象模型如下所示:

继承和派生习题

继承和派生习题 一、单选题 1.若类A和类B的定义如下: class A { int i,j; public: int geti(){return i;} }; class B: public A { int k; public: void make(){k=i*j;} }; 则上述中,()是非法的表达式。 A.k=i*j B.int k; C.return i; D.void make(); 2.关于公有继承,下列说法错误的是()。 A.基类的公有成员和保护成员被继承后作为派生类的公有成员和保护成员。 B.派生类的其他成员可以直接访问基类的公有成员和保护成员。 C.派生类的对象可以访问基类的私有成员。 D.派生类成员和对象都无法访问基类的私有成员。 3.下列说法中错误的是()。 A.公有继承时基类中的public成员在派生类中仍是public的; B.公有继承时基类中的private成员在派生类中仍是private的; C.私有继承时基类中的public成员在派生类中是private的; D.保护继承时基类中的public成员在派生类中是protected的; 4.在公有继承中,基类中的公有成员作为派生类的()。 A.公有成员 B.私有成员 C.保护成员 D.私有成员函数 5.基类中的()不允许外界访问,但允许派生类的成员访问,这样既有一定的隐藏能力,又提供了开放的接口。 A.公有成员 B.私有成员 C.保护成员 D.私有成员函数 6.在下面的表达式中,不表示虚继承的是()。 A.virtual public

B.public virtual C.public D.virtual 7.设置虚函数的声明中,正确的是()。 A.简化程序 B.消除二义性 C.提高运行效率 D.减少目标代码 8.下面叙述错误的是()。 A.基类的protected成员在派生类中仍然是protected B.基类的protected成员在public派生类中仍然是protected的 C.基类的protected成员在private派生类中是private的 D.基类的protected成员不能被派生类的对象访问 9.派生类的对象对它的基类成员中()是可以访问的。 A.公有继承的公有成员 B.公有继承的私有成员 C.公有继承的保护成员 D.私有继承的公有成员 10.下列对派生类的描述中,错误的是()。 A.一个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的缺省继承方式是private D.派生类只含有基类的公有成员和保护成员 11.继承具有(),即当基类本身也是某一个类派生类时,底层的派生类也会自动继承间接基类的成员。 A.规律性 B.传递性 C.重复性 D.多样性 12.设有基类定义: class Cbase { private: int a; protected: int b; public: int c; }; 派生类采用何种继承方式可以使成员变量b成为自己的私有成员( ) A.私有继承 B.保护继承

c++继承与派生习题答案

继承与派生 概念填空题 1.1在C++中,三种派生方式的说明符号为public、private、protected不加说明,则默认的派生方式为private。 1.2当公有派生时,基类的公有成员成为派生类的公有成员;保护成员成为派生类的保护成员;私有成员成为派生类的不能直接访问成员。当保护派生时,基类的公有成员成为派生类的保护成员;保护成员成为派生类的保护成员;私有成员成为派生类的不能直接访问成员。 1.3 派生类的构造函数一般有3项工作要完成:首先基类初始化,其次成员对象初始化,最后执行派生类构造函数体。 1.4多继承时,多个基类中的同名的成员在派生类中由于标识符不唯一而出现二义性。在派生类中采用虚基类或作用域分辨符来消除该问题。 3.选择题 3.1下面对派生类的描述中,错误的是(D )。 A.一个派生类可以作为另外一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类的成员 D.派生类中继承的基类成员的访问权限到派生类中保持不变 3.2下列对友元关系叙述正确的是(A)。 A.不能继承 B.是类与类的关系 C.是一个类的成员函数与另一个类的关系 D.提高程序的运行效率 3.3当保护继承时,基类的(B)在派生类中成为保护成员,不能通过派生类的对象来直接访问。 A.任何成员B.公有成员和保护成员 C.公有成员和私有成员D.私有成员 3.4设置虚基类的目的是(B)。 A.简化程序B.消除二义性 C.提高运行效率D.减少目标代码 3.5在公有派生情况下,有关派生类对象和基类对象的关系,不正确的叙述是(C )。 A.派生类的对象可以赋给基类的对象 B.派生类的对象可以初始化基类的引用 C.派生类的对象可以直接访问基类中的成员 D.派生类的对象的地址可以赋给指向基类的指针 3.6有如下类定义: class MyBASE{ int k; public: void set(int n) {k=n;} int get( ) const {return k;}

继承与多态作业题

继承与多态习题 一.基本概念与基础知识自测题 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"<

相关文档