Upload
finian-lau
View
478
Download
1
Embed Size (px)
Citation preview
类的继承
定义继承层次
确定层次权限
访问基类成员
构造函数和析构函数
虚拟函数
多继承
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 的可 性视
访问基类成员为了用已被派生类重用的名字来访问基类的成员,我们必须
用它的类域操作符限定修饰基类的成员。
voidShy::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; }
inlineNameQuery::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( "firebird" ); 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( "dumbo" );// 通过虚拟机制动态调用 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()
十分感谢各位听讲,错误之处,请不吝指出!
谢 谢