1 / 66

高级语言程序设计 第四章 C ++语言基础-继承类和派生类

高级语言程序设计 第四章 C ++语言基础-继承类和派生类. 周律. 基类和派生类 单继承 可访问性,继承方式,继承后的构造函数 多继承 虚基类. 继承 :重新使用接口 , 高效的重用代码方法。. 基类和派生类. 继承是类之间定义的一种重要关系。 定义类 B 时,自动得到类 A 的操作和数据属性,使得程序员只需定义类 A 中所没有的新成分就可完成在类 B 的定义,这样称类 B 继承了类 A ,类 A 派生了类 B , A 是基类(父类), B 是派生类(子类)。这种机制称为继承。 称已存在的用来派生新类的类为 基类 ,又称为 父类 。

kimo
Download Presentation

高级语言程序设计 第四章 C ++语言基础-继承类和派生类

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 高级语言程序设计第四章 C++语言基础-继承类和派生类 周律

  2. 基类和派生类 • 单继承 • 可访问性,继承方式,继承后的构造函数 • 多继承 • 虚基类

  3. 继承 :重新使用接口,高效的重用代码方法。

  4. 基类和派生类 继承是类之间定义的一种重要关系。定义类B时,自动得到类A的操作和数据属性,使得程序员只需定义类A中所没有的新成分就可完成在类B的定义,这样称类B继承了类A,类A派生了类B,A是基类(父类),B是派生类(子类)。这种机制称为继承。 称已存在的用来派生新类的类为基类,又称为父类。 由已存在的类派生出的新类称为派生类,又称为子类。

  5. B1,B2的基类 A A的派生类 C1,C2,C3的基类 A的派生类(单继承) C3的基类 B1 B2 B1的派生类 B1,B2的派生类(多继承) C1 C2 C3 类之间的继承与派生关系 基类和派生类

  6. 派生类的定义_单继承 • 单继承的定义格式如下: class<派生类名> :<继承方式><基类名> { public: //派生类新定义成员 members; <private:> members; <protected:> members; };

  7. 派生类的定义_单继承 其中,<派生类名>是新定义的一个类的名字,它是从<基类名>中派生的,并且按指定的<继承方式>派生的。 <继承方式>常作用如下三种关键字给予表示: public:表示公有继承; private:表示私有继承,可默认声明; protected:表示保护继承。

  8. 派生类的定义_多继承 • 多继承的定义格式如下: class<派生类名> :<继承方式1><基类名1>,<继承方式2><基类名2>,… { public: //派生类新定义成员 members; <private:> members; <protected:> members; };

  9. 类: Person Name, Age,Sex 类: Student Stu_No, Score,ClassId, getScore() 单继承 • 在类层次结构中,派生类继承了基类的方法和变量 • 它们也可有其自身的属性和方法

  10. 优点 • 最重要优点:代码的可重用性 • 一旦创建了基类,它不需要做任何更改就能在不同的情况下使用 • 代码可重用的结果是类库的开发 • 类库由封装在类中的数据和方法组成 • 通过从现有类派生一个类可重新定义基类的成员函数,还可将新成员添加到派生类中 • 在整个过程中,基类保持不变

  11. 直接基类和间接基类 • 如果某个基类在基类列表中提及,则称它是直接基类。例如:: class A { }; class B:public A { }; //其中,类A 为直接基类。 • 可将间接基类写为: class A { }; class B:public A { }; class C:public B { }; //可扩展到任意级数

  12. 可访问性 • 在类之类成员总可被它们自己类中的成员函数访问,而不论这些成员是 private,还是 public。 • 外定义的对象仅当类成员为public 时方可访问它们 • 例如,如果emp1是Employee类的实例,且display()是 Employee的成员函数,则在main()中语句 emp1.display(); 在 display()是 public 成员时,是有效的。 • 对象 emp1不能访问Employee类的私有成员

  13. 访问基类成员 凭借继承: • 派生类的成员函数在基类的成员为public 时能访问这些成员。 • 派生类成员不能访问基类的private 成员。

  14. Protected 访问说明符 从作用域和访问方面来说,protected 部分与private 部分类似。 • protected 成员仅可被所属类的成员访问。 • protected 成员不能由来自该类外部的对象或函数访问,如main()。 • protected 成员与private 成员的区别仅出现在派生类中

  15. 示例 class Employee //基类 { private: int privA; protected: int protA; public: int pubA; };

  16. 示例 class Manager : public Employee //派生类 { public: void fn() { int a; a = privA; //错误:不可访问 a = protA; //有效 a = pubA; //有效 } };

  17. 示例 void main() { Employee emp; //基类对象 emp.privA = 1; //错误:不可访问 emp.protA = 1; //错误:不可访问 emp.pubA = 1; //有效 Manager mgr; //派生类对象 mgr.privA = 1; //错误:不可访问 mgr.protA = 1; //错误:不可访问 mgr.pubA = 1; //有效 }

  18. 继承类型 1. 公有继承(public)。 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。 2. 私有继承(private)。 私有继承的特点是基类的公有成员和保护成员作为派生类的私有成员,并且不能被这个派生类的子类访问。 3. 保护继承(protected)。 保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

  19. 继承类型示例 class A //基类 { private: int privA; protected: int protA; public: int pubA; }; class B : public A //公共派生类 { public: void fn() { int a; a = privA; //错误:不可访问 a = protA; //有效 a = pubA; //有效 } };

  20. 示例 class C : private A //私有派生类 { public: void fn() { int a; a = privA; //错误:不可访问 a = protA; //有效 a = pubA; //有效 } }; void main() { int m; B obj1; //公共私有派生类对象

  21. 示例 • m = obj1.privA; //错误:不可访问 m = obj1.protA; //错误:不可访问 m = obj1.pubA; //有效:B 是从A 公共派生的 C obj2; //私有派生类的对象 m = obj2.privA; //错误:不可访问 m = obj2.protA; //错误:不可访问 m = obj2.pubA; //错误:不可访问:不可访问:C 是从类A 私有派生的 } 注意: 如果创建类时未给定访问说明符,则假定其为private

  22. 继承类型 • 派生类中的函数能访问基类中的protected 和public 成员 • 类外的或main() 中的派生类的对象不能访问基类的private 或protected 成员 • 公有派生类和私有派生类之间的不同点: • 类B(从A 公有派生)的对象可以访问基类的public 成员。 • 但是,类C(它从A 私有派生)的对象则不能访问基类的任何成员

  23. 继承类型 • Protected 派生类中的函数能够访问基类的protected 和public 成员。但是,派生类(在main 中或在类外)的对象则不能访问基类的任何成员

  24. 继承方式 基类特性 派生类特性 公有继承 public public protected protected private 不可访问 私有继承 public private protected private private 不可访问 保护继承 public protected protected protected private 不可访问 派生类的继承方式

  25. 多级继承 派生类的类型(即public、private 或protected)将影响派生类函数对多级继承中的基类的成员的访问

  26. 多级继承示例 • 在下面的代码中,类B从类A私有派生,类C依次从类B 公有派生。 class A { public : int a; }; class B : private A { public : int b; void func_b() { int x,y; x=a; // 有效 y=b; // 有效 } };

  27. 示例(续) class C : public B { public : void func_c() { int x,y; x=a; // 无效 y=b; // 有效 } };

  28. 继承下的构造函数

  29. 单继承下的构造函数 派生类构造函数的一般格式如下: <派生类名>(<派生类构造函数总参数表>) :<基类构造函数>(<参数表1>), <子对象名>(<参数表2>) { <派生类中数据成员初始化> }; 派生类构造函数的调用顺序如下: (1)调用基类的构造函数,调用顺序按照它们继承时说明的顺序。 (2)调用子对象类的构造函数,调用顺序按照它们在类中说明的顺序。 (3)派生类构造函数体中的内容。

  30. 举例 • 首先调用的是对象基本部分的构造函数,然后调用派生类的适当构造函数。 class Base { protected: int a; public: Base() //默认构造函数 { a = 0; } Base(int c) //单参数构造函数 { a = c; } }; class Derived:public Base { public: Derived():Base(){};//默认构造函数 Derived(int c):Base(c){}; //单参数构造函数 };

  31. 构造函数 • 在调用派生类的构造函数过程中,可明确选择应调用基类的哪一个构造函数。 Derived obj1(20); 使用Derived 中的一个参数的构造函数。这一构造函数也调用基类中的相应构造函数 。 Derived(int c):Base(c); //传递参数 c 给Base

  32. 构造函数 • 当声明派生类的某一对象时,用语句: Derived obj; 首先调用使基类的构造函数,然后调用派生类的构造函数 • 基类构造函数在派生类构造函数后给出,并用冒号分隔,如: Derived(): Base(){}

  33. 构造函数_注意的问题 注意的问题 在实际应用中,使用派生类构造函数时应注意如下几个问题: (1)派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有默认的构造函数或者根本没有定义构造函数。当然,基类中没有定义构造函数,派生类根本不必负责调用基类构造函数。 (2)当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数途径。在有的情况下,派生类构造函数体可能为空,仅起到参数传递作用。

  34. 析构函数 • 析构函数以与构造函数相反的顺序调用 • 析构函数首先为派生类调用,然后为基类调用。 • 仅当派生类的构造函数通过动态内存管理分配内存时才定义派生类的析构函数。 • 如果派生类的构造函数不起任何作用或派生类中未添加任何附加数据成员,则派生类的析构函数可以是一个空函数

  35. 调用成员函数 • 派生类中的成员函数与基类中的成员函数可以有相同的名称 • 当使用基类的对象调用函数时,基类的函数被调用 • 当您使用派生类对象的名称时,派生类的函数被调用 • 如果派生类的成员函数要调用相同名称的基类函数,它必须使用作用域运算符

  36. 调用成员函数 • 基类中的函数既可使用基类的对象,也可使用派生类的对象调用 • 如果函数存在于派生类而不是基类中,那么它只能被派生类的对象调用

  37. 举例 class Base { protected: int ss; public: int func() { return ss; } }; class Derived: public Base { public: int func() { return Base::func(); } };

  38. 举例 void main() { Base b1; //基类对象 b1.func(); //调用基类函数 func Derived a1; //派生类对象 a1.func(); //调用派生类对象 func }

  39. 多重继承 • 多重继承是从多个基类中创建一个新类的过程。 派生类继承了两个或多个基类的特性。 • 多重继承可以将多个基类的行为合并到一个类中。 • 多重继承层次结构代表了其基类的组合

  40. 基类 Teacher Student Teaching assistant 多重继承

  41. 多重继承 • 多重继承语法和单继承语法相似 class Teacher { }; class Student { }; class Teach_asst: public Teacher, public Student { }; • 提供两个基类的名称,通过逗号分开。 • 继承和多重继承访问的规则和单继承相同

  42. 多重继承下的构造函数 派生类构造函数执行顺序是先执行所有基类的构造函数,再执行派生类本身构造函数。处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的各基类顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序无关。 多继承下派生类的构造函数与单继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。同时,派生类的参数个数必须包含完成所有基类初始化所需的参数个数。

  43. 多重继承下的构造函数 多继承的构造函数 在多继承的情况下,多个基类构造函数的调用次序是按基类在被继承时所声明的次序从左到右依次调用,与它们在派生类的构造函数实现中的初始化列表出现的次序无关。 派生类的构造函数格式如下: <派生类名>(<总参数表>):<基类名1>(<参数表1>),<基类名2>(<参数表2>),…<子对象名>(<参数表n+1>),… { <派生类构造函数体> } 其中,<总参数表>中各个参数包含了其后的各个分参数表。

  44. 举例 class Teacher { private: int x; public: Teacher() //构造函数 {x =0;} Teacher(int s) {x = s;} }; class Student { private: int y; public: Student() //构造函数 {y = 0;} Student(int a) {y = a;} };

  45. 举例 class Teach_asst: public Teacher,public Student { private: int z; public: Teach_asst():Teacher(),Student() //构造函数 { z = 0; } Teach_asst(int s,int a,int b):Teacher(s),Student(a) { z = b; } };

  46. 构造函数 • 必须定义构造函数以初始化所有类的数据成员。 • 就像下面的语句中一样,基类构造函数的名称位于冒号之后,各名称之间以逗号分开。 Teach_asst():Teacher(),Student(); • 如果构造函数有参数: Teach_asst(int s, //Teacher 类参数 int a, //Student 类参数 int b)://本类参数 Teacher(s), //调用Teacher构造函数 Student(a)//调用 Student 构造函数 { //设置本类的数据成员 z = b; }

  47. 构造函数和析构函数 • 调用构造函数的一般顺序是: • 基础类按照它们在基类列表中的顺序显示:Teacher, Student • 如果类中有成员对象,则按照它们在派生类声明中的顺序,对它们进行初始化。 • 对象本身(使用其构造函数代码) • 调用析构函数的一般顺序是: • 首先调用类的析构函数,然后是那些成员对象的析构函数,接着是基类的析构函数

  48. 多继承_二义性和支配原则 二义性和支配原则 一般说来,在派生类中对基类成员的访问应该是惟一的。但是,由于多继承情况下,可能造成对基类中某个成员的访问出现了不惟一的情况,则称为对基类成员访问的二义性问题。 1. 同名成员的二义性 在多重继承中,如果不同基类中有同名的函数,则在派生类中就有同名的成员,这种成员会造成二义性。

  49. 举例 如:class A { public: void f(); }; class B { public: void f(); void g(); }; class C: public A,public B { public: void g(); void h(); }; C obj; …… 则对函数f()的访问是二义的:obj.f(); //无法确定访问A中或是B中的f()

  50. 二义性 使用基类名可避免这种二义: obj.A::f();//A中的f(); obj.B::f();//B中的f(); C类的成员访问f()时也必须避免这种二义。 以上这种用基类名来控制成员访问的规则称为支配原则。 例如: obj.g();//隐含用C的g()obj.B::g();//用B的g() 以上两个语句是无二义的。

More Related