430 likes | 596 Views
面向对象编程 : C++. 继 承. 5. 课题目标. 描述单继承 描述基类和派生类 访问基类成员及使用类中指针 描述继承类型 描述继承下的构造函数和析构函数 描述如何调用基类和派生类的成员函数 描述容器类. 单继承. 为了易于维护和重用类对象,我们需能够将相似特征的类关联到另一类中 单继承 是从现有的一个基类创建新类的过程 例如,让我们考虑这样一个程序,在这一程序中,我们要处理与某一组织中雇用人员相关的事情. 单继承(续). 每个子类被认为是由 Employee 类派生而来。 Employee 类称为 基 类,而新创建的类则称为 派生类.
E N D
面向对象编程: C++ 继 承 5
课题目标 • 描述单继承 • 描述基类和派生类 • 访问基类成员及使用类中指针 • 描述继承类型 • 描述继承下的构造函数和析构函数 • 描述如何调用基类和派生类的成员函数 • 描述容器类
单继承 • 为了易于维护和重用类对象,我们需能够将相似特征的类关联到另一类中 • 单继承是从现有的一个基类创建新类的过程 • 例如,让我们考虑这样一个程序,在这一程序中,我们要处理与某一组织中雇用人员相关的事情
单继承(续) • 每个子类被认为是由Employee 类派生而来。Employee 类称为基类,而新创建的类则称为派生类
类: Employee Name, Age, Emp_id Salary, Department 类: Manager m_name, m_age, m_Emp_id, m_salary m_department perks, no_of_employees reporting 单继承(续) • 在类层次结构中,派生类继承了基类的方法和变量 • 它们也可有其自身的属性和方法
优点 • 最重要优点:代码的可重用性 • 一旦创建了基类,它不需要做任何更改就能在不同的情况下使用 • 代码可重用的结果是类库的开发 • 类库由封装在类中的数据和方法组成 • 通过从现有类派生一个类可重新定义基类的成员函数,还可将新成员添加到派生类中 • 在整个过程中,基类保持不变
基类和派生类 • 派生能以图形用从派生类到基类的箭头来表示 • 箭头指向基类表示派生类引用基类中的函数和数据,而基类则不能访问派生类
基类和派生类(续) • 单独派生类的声明与任一普通类的声明类似 • 我们还必须给出基类的名称,例如: class Manager : public Employee • 任一类均可用作基类 • 基类可分为两类: • 直接基类 • 间接基类
直接基类和间接基类 • 如果某个基类在基类列表中提及,则称它是直接基类。例如:: class A { }; class B:public A { }; //其中,类A 为直接基类。 • 可将间接基类写为: class A { }; class B:public A { }; class C:public B { }; //可扩展到任意级数
可访问性 • 可访问性:当派生类的对象能使用基类的成员函数或数据成员时,即可知道继承的属性。 • 类成员总可被它们自己类中的成员函数访问,而不论这些成员是private,还是 public。 • 在类之外定义的对象仅当类成员为public 时方可访问它们
访问基类成员 • 凭借继承: • 派生类的成员函数在基类的成员为public 时能访问这些成员。 • 派生类成员不能访问基类的private 成员。 • 例如,如果emp1是Employee类的实例,且display()是 Employee的成员函数,则在main()中语句 emp1.display(); 在 display()是 public 成员时,是有效的。 • 对象 emp1不能访问Employee类的私有成员
Protected 访问说明符 • 从作用域和访问方面来说,protected 部分与private 部分类似。 • protected 成员仅可被所属类的成员访问。 • protected 成员不能由来自该类外部的对象或函数访问,如main()。 • protected 成员与private 成员的区别仅出现在派生类中
访问基类成员(续) • 派生类的成员能访问基类的public 和protected 成员;它们不能访问基类的private 成员。 • 符合信息隐藏的面向对象概念。 • 不希望任何人访问某些类成员,这些成员可放在private 部分。 • 可通过提供一些protected 成员允许受到控制的访问。 • 继承没有逆运算
示例 class Employee //基类 { private: int privA; protected: int protA; public: int pubA; };
示例(续) class Manager : public Employee //派生类 { public: void fn() { int a; a = privA; //错误:不可访问 a = protA; //有效 a = pubA; //有效 } };
示例(续) • void main() { Employee emp; //基类对象 emp.privA = 1; //错误:不可访问 emp.protA = 1; //错误:不可访问 emp.pubA = 1; //有效 Manager mgr; //派生类对象 mgr.privA = 1; //错误:不可访问 mgr.protA = 1; //错误:不可访问 mgr.pubA = 1; //有效 }
类指针 • 可以使用已被声明指向一个类的指针来引用另一个类。 • 如果派生类有public 基类,那么指向派生类的指针可赋给指向基类的指针类型变量。 • 例如,因为Manager是一个 Employee,Manager*可用作 Employee*。然而,Employee*不能用作 Manager*
指针示例 • Avoid main() { Manager mgr; Employee* emp = &mgr;//有效:每个经理都是雇员 Employee eml; Manager* man = &eml;//错误:不是每个雇员都是经理 } • 当通过指针进行操作时,派生类的某个对象可看作其基类的某个对象。但是,反过来则不成立
继承类型 • 派生类能用 public、private 和protected 说明符中的某一个加以声明 • 在派生类的类声明中,关键字public 指定派生类的对象能访问基类的public 成员函数 • 使用派生类声明中的private 关键字,main() 中的派生类对象则不能访问基类的public 成员函数
继承类型示例 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; //有效 } };
示例(续) class C : private A //私有派生类 { public: void fn() { int a; a = privA; //错误:不可访问 a = protA; //有效 a = pubA; //有效 } }; void main() { int m; B obj1; //公共私有派生类对象
示例(续) • 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
继承类型(续) • 派生类中的函数能访问基类中的protected 和public 成员 • 类外的或main() 中的派生类的对象不能访问基类的private 或protected 成员 • 公有派生类和私有派生类之间的不同点: • 类B(从A 公有派生)的对象可以访问基类的public 成员。 • 但是,类C(它从A 私有派生)的对象则不能访问基类的任何成员
继承类型(续) • Protected 派生类中的函数能够访问基类的protected 和public 成员。但是,派生类(在main 中或在类外)的对象则不能访问基类的任何成员
派生类的可访问性 • 有一种很轻松就能记住此表的方法: • 首先,派生类无权访问基类的private 成员。 • 第二,公有继承基类均不改变派生类从基类继承的成员的访问级别。 • 基类的其它两个访问级别使所有继承的成员与基类( private 基类为private,protected 基类为protected )有相同的访问级别
多级继承 • 派生类的类型(即public、private 或protected)将影响派生类函数对多级继承中的基类的成员的访问
多级继承示例 • 在下面的代码中,类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; // 有效 } };
示例(续) class C : public B { public : void func_c() { int x,y; x=a; // 无效 y=b; // 有效 } };
继承下的构造函数 • 首先调用的是对象基本部分的构造函数,然后调用派生类的适当构造函数。 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){}; //单参数构造函数 };
构造函数(续) • 当声明派生类的某一对象时,用语句: Derived obj; 首先调用使基类的构造函数,然后调用派生类的构造函数 • 基类构造函数在派生类构造函数后给出,并用冒号分隔,如: Derived(): Base(){}
构造函数(续) • 在调用派生类的构造函数过程中,可明确选择应调用基类的哪一个构造函数。 Derived obj1(20); 使用Derived 中的一个参数的构造函数。这一构造函数也调用基类中的相应构造函数 。 Derived(int c):Base(c); //传递参数 c 给Base
析构函数 • 析构函数以与构造函数相反的顺序调用 • 析构函数首先为派生类调用,然后为基类调用。 • 仅当派生类的构造函数通过动态内存管理分配内存时才定义派生类的析构函数。 • 如果派生类的构造函数不起任何作用或派生类中未添加任何附加数据成员,则派生类的析构函数可以是一个空函数
调用成员函数 • 派生类中的成员函数与基类中的成员函数可以有相同的名称 • 当使用基类的对象调用函数时,基类的函数被调用 • 当您使用派生类对象的名称时,派生类的函数被调用 • 如果派生类的成员函数要调用相同名称的基类函数,它必须使用作用域运算符
调用成员函数(续) • 基类中的函数既可使用基类的对象,也可使用派生类的对象调用 • 如果函数存在于派生类而不是基类中,那么它只能被派生类的对象调用
调用成员函数(续) class Base { protected: int ss; public: int func() { return ss; } }; class Derived: public Base { public: int func() { return Base::func(); } };
调用成员函数(续) void main() { Base b1; //基类对象 b1.func(); //调用基类函数 func Derived a1; //派生类对象 a1.func(); //调用派生类对象 func }
容器类 • 继承可称为“是一个”关系。 • 雇员的例子:管理者是一个雇员,或秘书也是一个雇员等等。 • 相反,具有另一个类Y 的成员的类 X 可被说成是有一个Y,或X 包含Y。这一关系称为成员关系,或“有一个”关系。 class X //X 包含 Y { public: Y abc; };
容器类(续) • 当某个类将另一个类的对象作为其成员包括在内时,它称为容器类 • 喷气式飞机的例子 :考虑从称为engine 的类为喷气式飞机派生一个类。喷气式飞机不是一台引擎,但是,它有一台引擎 • 使用继承还是使用成员关系:问一架喷气式飞机是否有多个引擎。若是,则这个关系最有可能是“有一个”关系,而不是“是一个”关系
容器类的构造函数 class engine { private: int num; public: engine(int s) { num = s; } }; class jet { private: int jt; engine eobj; //这里声明一个对象 public: jet(int x, int y): eobj(y) { jt = x; } };
构造函数(续) • 在jet类的构造函数中,engine 类对象的名字写在冒号的后面。它告诉编译器用y值初始化jet 类的 eobj 数据成员。这就与用下面的语句声明类engine 的对象类似 engine eobj(y); • 任何数据类型的变量均能这样初始化
在jet类的构造函数中,engine 类对象的名字写在冒号的后面。它告诉编译器用y值初始化jet 类的 eobj 数据成员。这就与用下面的语句声明类engine 的对象类似。 engine eobj(y); • 任何数据类型的变量均能这样初始化。
本课总结 • 继承是从现有的一个类创建新类的过程 • 派生类依次可以是另一个类的基类 • 派生类能用访问说明符,即public、private和protected加以说明 • 首先调用的是对象基本部分的构造函数,然后调用派生类的适当构造函数 • 析构函数以与构造函数相反的顺序调用 • 当某个类将另一个类的对象作为其成员包括在内时,称为容器类