SlideShare a Scribd company logo
类的继承 定义继承层次 确定层次权限 访问基类成员 构造函数和析构函数 虚拟函数 多继承 2008.9.19
定义继承层次 继承关系通过类派生表( class derication list )来指定, 在单继承下它的一般形式为: derived class: access-level base-class 这里 access-level  是 public protected  或 private  之一。 class Query; class NameQuery : public Query { ...}  class NameQuery : public Query; 在派生表中指定的类必须首先被定义好方可被指定为基类。 派生类的前向声明不能包括它的派生表而只是类名。 class Query; class NameQuery;  ( ∨ )
确定层次权限 成员权限 把一个成员指定为 public  的标准,在基于对象和面向对象的设计之间没有区别。真正的变化在于是把一个非公有成员声明为 protected  还是 private ,如果我们希望防止后来的派生类直接访问成员,则把它声明为 private (对基类而言)。如果我们认为一个成员为后来的要求直接访问该成员的派生类提供了一个操作或数据存储,以使派生类的实现更为有效,则把这个成员声明为 protected 。在设计一个基类时设计者还要考虑的是确定哪些成员函数是类型相关的,它们是类层次结构中的虚拟函数。
确定层次成员 基类权限 一个 private  基类反映了一种并非基于子类型关系的继承形式,基类的整个公有接口在派生类中变成 private 。 private  派生被称为实现继承( implementation inheritance )派生类不直接支持基类的公有接口,相反当它提供自己的公有接口时,它希望重用基类的实现。 class PeekbackStack : private IntArray { public: //  维持公有访问级别 using IntArray::size; // ... }; 派生类只能将继承得到的成员恢复到原来的访问级别,该访问级别不能比基类中原来指定的级别更严格或更不严格。
确定层次成员 基类权限 第三种派生形式是 protected  继承在 protected ,继承下基类的所有公有成员都成为派生类的 protected 成员
访问基类成员 每个基类代表了一个由该基类的非静态数据成员组成的子对象 subobject ,派生类对象由其基类子对象以及由派生类的非静态数据成员构成的派生部分组成。所有派生类对象都引用相同的单一的共享的静态成员。 class Diffident  {  public: // ... protected: int _mumble; // ... }; class Shy : public Diffident  { public: // ... protected: string _mumble; // ... }; 隐藏了  Diffident::_mumble  的可视性
访问基类成员 为了用已被派生类重用的名字来访问基类的成员,我们必须用它的类域操作符限定修饰基类的成员。 void Shy:: turn_eyes_down() { // ... _mumble = "excuse me"; // ok // ok:  限定修饰基类的实例 Diffident::_mumble = -1; }
访问基类成员 class Diffident { public: void mumble( int softness ); // ... }; class Shy : public Diffident { public: void mumble( string whatYaSay ); void print( int soft, string words ); // ... }; 隐藏了  Diffident::mumble  的可视性,没有形成一对重载实例。 Shy simon; simon.mumble( "pardon me" ); simon.mumble( 2 );
访问基类成员 class Shy : public Diffident { public: // ok:  在标准  C++  下通过  using  声明 //  创建了基类和派生类成员的重载集合 void mumble( string whatYaSay ); using Diffident::mumble; // ... }; 针对一个成员函数的 using  声明不能指定参数表,只能指定成员函数名,这意味着如果该函数在基类中被重载,则所有的重载实例都被加入到派生类类型的域中。我们不能只增加基类的重载成员函数集中的一个实例。
构造函数和析构函数 基类构造函数 如果有多个基类则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。 成员类对象构造函数,如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。 派生类构造函数 派生类并不继承基类的构造函数,每个派生类都必须提供自己的构造函数集。 派生类构造函数只能合法地调用其直接基类的构造函数。 析构函数的调用顺序与构造函数相反
构造函数和析构函数 class NameQuery : public Query { public: // ... protected: bool _present; string _name; }; inline NameQuery::NameQuery() {  _present = false;  } inline NameQuery:: NameQuery( const string &name, vector<location> *ploc ) : _name( name ), Query( *ploc ), _present( true ) {…}
虚拟函数 void display( Query *pb ) { pb->display(); } class Query  { public: virtual   ostream& print( ostream& = cout ) const; // ... }; 要把对象声明为虚拟的我们只需指定关键字 virtual:  引入虚拟函数的类必须定义它或者把它声明为 纯虚拟函数 。 为了使虚拟函数的派生类实例能够改写其基类的活动实例 它的原型必须与基类完全匹配。 派生类实例的返回值可以是基类实例返回类型的公有派生类类型。
虚拟函数 当成员函数是虚拟的时候,通过一个类对象(指针或引用)而被调用的该成员函数,是在该类对象的动态类型中被定义的成员函数。但是,正如所发生的一个类对象的静态和动态类型是相同的,所以虚拟函数机制只在使用指针和引用时才会如预期般地起作用,只有在通过基类指针或引用间接指向派生类子类型时 多态性 ( 动态绑定 ) 才会起作用,使用基类对象并不会保留派生类的类型身份。
虚拟函数 void print( Query object,const Query *pointer,const Query &reference ) { //  直到运行时刻才能确定 //  调用哪个  print()  实例 pointer->print(); reference.print(); //  总是调用  Query::print() object.print(); } int main() { NameQuery firebird( &quot;firebird&quot; ); print( firebird, &firebird, firebird ); }
虚拟函数 纯虚拟函数( pure virtual function )   通过它可以指明一个虚拟函数只是提供了一个可被子类型改写的接口,但是它本身并不能通过虚拟机制被调用。 class Query { public: //  声明纯虚拟函数 virtual ostream& print( ostream&=cout ) const = 0; // ... }; Query *pq2 = new Query; 包含或继承一个或多个纯虚拟函数的类被编译器识别为抽象基类,试图创建一个抽象基类的独立类对象会导致编译时刻错误,类似地,通过虚拟机制调用纯虚拟函数也是错误的。
虚拟函数 纯虚拟函数( pure virtual function )   当用类域操作符调用虚拟函数时,我们改变了虚拟机制,使得虚拟函数在编译时刻被静态解析。 假设我们已经为 Query  层次结构的所有基类和派生类定义了虚拟函数 isA() 。 Query *pquery = new NameQuery( &quot;dumbo&quot; ); //  通过虚拟机制动态调用  isA() //  调用  NameQuery::isA()  实例 pquery->isA(); //  在编译时刻静态调用  isA //  调用  Query::isA  实例 pquery->Query::isA();
多继承 多继承的一般形式如下: derived class: access-level base-class, access-level      base-class, …… 每个被列出的基类还必须指定其访问级别 public 、 protected  或 private 之一,与单继承一样,只有当一个类的定义已经出现后它才能被列在多继承的基类表中。
多继承 基类的 public  和 protected  成员可以直接被访问就像它们是派生类的成员一样,对多继承这也是正确的。但是在多继承下派生类可以从两个或者更多个基类中继承同名的成员。然而在这种情况下直接访问是二义的将导致编译时刻错误。   ying_yang.print( cout );
多继承 class Bear : public ZooAnimal { public: virtual ~Bear(); virtual ostream&print( ostream&) const; virtual string isA() const; // ... }; class Endangered { public: virtual ~Endangered(); virtual ostream&print( ostream&) const; virtual void highlight() const; // ... }; class Panda :  public Bear, public Endangered { public: virtual ~Panda(); virtual ostream&print( ostream&) const; virtual void cuddle(); // ... }; 为了了解多继承怎样影响虚拟函数机制让我们为每个 Panda  的直接基类定义一组虚拟函数 。
多继承
多继承 当用 Panda  类对象的地址初始化或赋值 Bear  或 ZooAnimal  指针或引用时, Panda  接口中 Panda  特有的部分以及 Endangered  部分就都不能再被访问。例如 Bear *pb = new Panda; pb->print( cout ); // ok: Panda::print(ostream&) pb->isA(); // ok: Bear::isA() pb->cuddle(); //  错误 :  不是  Bear  接口的部分 pb->highlight(); //  错误 :  不是  Bear  接口的部分 delete pb; // ok: Panda::~Panda()
十分感谢各位听讲,错误之处,请不吝指出! 谢  谢

More Related Content

PDF
Jdbc中驱动加载的过程分析(上)
DOC
J2ee面试知识
DOC
Java华为面试题
PDF
Oracle的Constraint约束V1.1
PPTX
4, workflow tables & api
PDF
functional-scala
PDF
Spring中的object xml映射详解
PDF
Spring 2.x 中文
Jdbc中驱动加载的过程分析(上)
J2ee面试知识
Java华为面试题
Oracle的Constraint约束V1.1
4, workflow tables & api
functional-scala
Spring中的object xml映射详解
Spring 2.x 中文

Viewers also liked (8)

PPT
Genetic Algorithm
PPTX
Uttrakhand b school's
PPT
Materi 4 Information System Engineering Sim 1223511116853894 8
PPT
Kuliah1 Struktur Data V1.0
PPT
Fallsem2015 16 cp1699-20-jul-2015_rm01_stacks_and_queues
PPTX
Data structures 3
PPT
Input output streams
PPT
List Data Structure
Genetic Algorithm
Uttrakhand b school's
Materi 4 Information System Engineering Sim 1223511116853894 8
Kuliah1 Struktur Data V1.0
Fallsem2015 16 cp1699-20-jul-2015_rm01_stacks_and_queues
Data structures 3
Input output streams
List Data Structure
Ad

Similar to Class Inheritance (9)

PPTX
C++中级培训胶片
 
PPT
改善程序设计技术的50个有效做法
PPTX
xwz 2010-10-31
PDF
竞赛中C++语言拾遗
PPTX
8, polymorphism
PPTX
C++11综述/新特性描述/Overview of C++11 New Features
PPTX
9, interfaces
PDF
VC++ Programming Training Lecture in Control Lab 301 of YSU
PPT
C++中级培训胶片
 
改善程序设计技术的50个有效做法
xwz 2010-10-31
竞赛中C++语言拾遗
8, polymorphism
C++11综述/新特性描述/Overview of C++11 New Features
9, interfaces
VC++ Programming Training Lecture in Control Lab 301 of YSU
Ad

Class Inheritance

  • 1. 类的继承 定义继承层次 确定层次权限 访问基类成员 构造函数和析构函数 虚拟函数 多继承 2008.9.19
  • 2. 定义继承层次 继承关系通过类派生表( class derication list )来指定, 在单继承下它的一般形式为: derived class: access-level base-class 这里 access-level 是 public protected 或 private 之一。 class Query; class NameQuery : public Query { ...} class NameQuery : public Query; 在派生表中指定的类必须首先被定义好方可被指定为基类。 派生类的前向声明不能包括它的派生表而只是类名。 class Query; class NameQuery; ( ∨ )
  • 3. 确定层次权限 成员权限 把一个成员指定为 public 的标准,在基于对象和面向对象的设计之间没有区别。真正的变化在于是把一个非公有成员声明为 protected 还是 private ,如果我们希望防止后来的派生类直接访问成员,则把它声明为 private (对基类而言)。如果我们认为一个成员为后来的要求直接访问该成员的派生类提供了一个操作或数据存储,以使派生类的实现更为有效,则把这个成员声明为 protected 。在设计一个基类时设计者还要考虑的是确定哪些成员函数是类型相关的,它们是类层次结构中的虚拟函数。
  • 4. 确定层次成员 基类权限 一个 private 基类反映了一种并非基于子类型关系的继承形式,基类的整个公有接口在派生类中变成 private 。 private 派生被称为实现继承( implementation inheritance )派生类不直接支持基类的公有接口,相反当它提供自己的公有接口时,它希望重用基类的实现。 class PeekbackStack : private IntArray { public: // 维持公有访问级别 using IntArray::size; // ... }; 派生类只能将继承得到的成员恢复到原来的访问级别,该访问级别不能比基类中原来指定的级别更严格或更不严格。
  • 5. 确定层次成员 基类权限 第三种派生形式是 protected 继承在 protected ,继承下基类的所有公有成员都成为派生类的 protected 成员
  • 6. 访问基类成员 每个基类代表了一个由该基类的非静态数据成员组成的子对象 subobject ,派生类对象由其基类子对象以及由派生类的非静态数据成员构成的派生部分组成。所有派生类对象都引用相同的单一的共享的静态成员。 class Diffident { public: // ... protected: int _mumble; // ... }; class Shy : public Diffident { public: // ... protected: string _mumble; // ... }; 隐藏了 Diffident::_mumble 的可视性
  • 7. 访问基类成员 为了用已被派生类重用的名字来访问基类的成员,我们必须用它的类域操作符限定修饰基类的成员。 void Shy:: turn_eyes_down() { // ... _mumble = &quot;excuse me&quot;; // ok // ok: 限定修饰基类的实例 Diffident::_mumble = -1; }
  • 8. 访问基类成员 class Diffident { public: void mumble( int softness ); // ... }; class Shy : public Diffident { public: void mumble( string whatYaSay ); void print( int soft, string words ); // ... }; 隐藏了 Diffident::mumble 的可视性,没有形成一对重载实例。 Shy simon; simon.mumble( &quot;pardon me&quot; ); simon.mumble( 2 );
  • 9. 访问基类成员 class Shy : public Diffident { public: // ok: 在标准 C++ 下通过 using 声明 // 创建了基类和派生类成员的重载集合 void mumble( string whatYaSay ); using Diffident::mumble; // ... }; 针对一个成员函数的 using 声明不能指定参数表,只能指定成员函数名,这意味着如果该函数在基类中被重载,则所有的重载实例都被加入到派生类类型的域中。我们不能只增加基类的重载成员函数集中的一个实例。
  • 10. 构造函数和析构函数 基类构造函数 如果有多个基类则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。 成员类对象构造函数,如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。 派生类构造函数 派生类并不继承基类的构造函数,每个派生类都必须提供自己的构造函数集。 派生类构造函数只能合法地调用其直接基类的构造函数。 析构函数的调用顺序与构造函数相反
  • 11. 构造函数和析构函数 class NameQuery : public Query { public: // ... protected: bool _present; string _name; }; inline NameQuery::NameQuery() { _present = false; } inline NameQuery:: NameQuery( const string &name, vector<location> *ploc ) : _name( name ), Query( *ploc ), _present( true ) {…}
  • 12. 虚拟函数 void display( Query *pb ) { pb->display(); } class Query { public: virtual ostream& print( ostream& = cout ) const; // ... }; 要把对象声明为虚拟的我们只需指定关键字 virtual: 引入虚拟函数的类必须定义它或者把它声明为 纯虚拟函数 。 为了使虚拟函数的派生类实例能够改写其基类的活动实例 它的原型必须与基类完全匹配。 派生类实例的返回值可以是基类实例返回类型的公有派生类类型。
  • 14. 虚拟函数 void print( Query object,const Query *pointer,const Query &reference ) { // 直到运行时刻才能确定 // 调用哪个 print() 实例 pointer->print(); reference.print(); // 总是调用 Query::print() object.print(); } int main() { NameQuery firebird( &quot;firebird&quot; ); print( firebird, &firebird, firebird ); }
  • 15. 虚拟函数 纯虚拟函数( pure virtual function ) 通过它可以指明一个虚拟函数只是提供了一个可被子类型改写的接口,但是它本身并不能通过虚拟机制被调用。 class Query { public: // 声明纯虚拟函数 virtual ostream& print( ostream&=cout ) const = 0; // ... }; Query *pq2 = new Query; 包含或继承一个或多个纯虚拟函数的类被编译器识别为抽象基类,试图创建一个抽象基类的独立类对象会导致编译时刻错误,类似地,通过虚拟机制调用纯虚拟函数也是错误的。
  • 16. 虚拟函数 纯虚拟函数( pure virtual function ) 当用类域操作符调用虚拟函数时,我们改变了虚拟机制,使得虚拟函数在编译时刻被静态解析。 假设我们已经为 Query 层次结构的所有基类和派生类定义了虚拟函数 isA() 。 Query *pquery = new NameQuery( &quot;dumbo&quot; ); // 通过虚拟机制动态调用 isA() // 调用 NameQuery::isA() 实例 pquery->isA(); // 在编译时刻静态调用 isA // 调用 Query::isA 实例 pquery->Query::isA();
  • 17. 多继承 多继承的一般形式如下: derived class: access-level base-class, access-level base-class, …… 每个被列出的基类还必须指定其访问级别 public 、 protected 或 private 之一,与单继承一样,只有当一个类的定义已经出现后它才能被列在多继承的基类表中。
  • 18. 多继承 基类的 public 和 protected 成员可以直接被访问就像它们是派生类的成员一样,对多继承这也是正确的。但是在多继承下派生类可以从两个或者更多个基类中继承同名的成员。然而在这种情况下直接访问是二义的将导致编译时刻错误。 ying_yang.print( cout );
  • 19. 多继承 class Bear : public ZooAnimal { public: virtual ~Bear(); virtual ostream&print( ostream&) const; virtual string isA() const; // ... }; class Endangered { public: virtual ~Endangered(); virtual ostream&print( ostream&) const; virtual void highlight() const; // ... }; class Panda : public Bear, public Endangered { public: virtual ~Panda(); virtual ostream&print( ostream&) const; virtual void cuddle(); // ... }; 为了了解多继承怎样影响虚拟函数机制让我们为每个 Panda 的直接基类定义一组虚拟函数 。
  • 21. 多继承 当用 Panda 类对象的地址初始化或赋值 Bear 或 ZooAnimal 指针或引用时, Panda 接口中 Panda 特有的部分以及 Endangered 部分就都不能再被访问。例如 Bear *pb = new Panda; pb->print( cout ); // ok: Panda::print(ostream&) pb->isA(); // ok: Bear::isA() pb->cuddle(); // 错误 : 不是 Bear 接口的部分 pb->highlight(); // 错误 : 不是 Bear 接口的部分 delete pb; // ok: Panda::~Panda()