第十一章 继承与类的派生
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
虚拟继承和虚基类
class x{ protected: int a; public: x() { a = 10;} }; class x1: virtual public x{ // 虚拟继承 public: x1() { cout << a << endl;} }; class x2: virtual public x{ // 虚拟继承 public: x2() { cout << a << endl;} }; class y: x1, x2{ public: y() { cout << a << endl;} // ok };
• 在多继承中,派生类对基类成员的访问权和继 承性的规则与单继承相同.
多继承的构造函数与析构函数
• 多继承中,派生类的构造函数和析构函数调用时,也会 自动调用基类的构造函数和析构函数. • 基类构造函数的调用顺序与定义继承的顺序一致,析 构函数的调用顺序则相反。
class A{public: A(){ cout << “A().” << endl;} ~A(){ cout << “~A().” << endl;}}; class B{public: B(){cout << “B().” << endl;} ~B(){ cout << “~B().” << endl;}}; class C: B, A{ public: C(){ cout << “C().” << endl;} ~C(){ cout << “~C().” << endl;}}; main(){ C c; }
面向对象程序设计与C++
第十一章 继承与类的派生
1.派生类的概念
• 类是具有相同特征(属性特征和行为特征)的对 象集合的抽象。 • 继承是对象类的一种相关关系,具有如下性 质:
– 类间的共享特征 – 类间的细微区别 – 类间的层次结构
鱼
海水鱼
黄花鱼 带鱼
淡水鱼
鲤鱼 墨鱼
为什么使用继承
• 引入继承的目的在于重用已有代码。
printf(“\nconstruct B.”); } printf(“\ndestroy B.”); } };
construct A. construct B. destroy B. destroy A.
派生类的构造函数与析构函数
• 若基类构造函数带参数,必须在派生类的构造 函数中将参数传入.
class A{ int x; public: A(int xv) { x = xv;} }; class B: public A{ public: B(int xv):A(xv){} }; main(){ B b(0); }
int a, b;
}; class B: class A{ int x, y; }; sizeof(A) = 8; sizeof(B) = 16;
A a b B a b x y
2.派生类对基类成员的访问与继承
• 派生类对基类成员的访问权:在派生类的域范围内, 是否可以直接访问基类对象的成员. • 派生类对基类成员的继承:派生类对象是否具有(可 使用)基类中声明的成员
~A(): x = 1 ~A(): x = 2
4. 多继承
• 当一个类具有多个类所具有的特点,就可能 用到多继承。
交通工具
汽车
商品
B
A C B
A C
D
E 单继承
F
D
E
多继承
F
多继承的定义
• 多继承的定义如下:
class A{ /* … */ }; class B{/* … */ }; class C: A, B{ /* … */ }; // 对A,B都为私有派生 class C:public A, B{/* … */ }; // 对A公有派生,对B私有派生 class C:A, public B{/* … */ }; // 对A私有派生,对B公有派生 class C:public A, public B{/*…*/}; // 对A,B都为公有派生
虚拟继承和虚基类
• 当类之间的继承关系出现如下棱形关系时,如使用普 通继承,派生类中就会出现公共基类成员的多个副本。
x
x1
y
x2
• 为了使派生类中只保留公共基类成员的一个副本,需 要使用虚继承:
class x1: virtual public x{ /*…*/}; class x2: virtual public x{/*…*/};
派生类的构造函数与析构函数
• 若派生类中包含类对象成员,则构造函数调用顺序:基 类->成员类->派生类;析构函数调用顺序正好相反。
class A{protected: int x; public: A(int n){ x = n; cout << “A(): x = “ << x << endl;} ~A(){ cout << “~A(): x = “ << x << endl;} }; class B: public A{ A a; 输出: public: B(int n):a(1) ,A(0){ x = n; A(): x = 0 cout << “B(): x = “ << x << endl;} A(): x = 1 ~B(){ cout << “~B(): x = “ << x << endl;} };B(): x = 2 ~B(): x = 2 main(){ B b(2); }
派生类对基类的访问与继承
class base public private protected
访问性 继承性
class derive1:base public private protected
public
class derive2: public:derive1 private protected
派生类对基类成员的继承性
• 派生类对基类成员的继承性(也就是派生类中是否可 使用基类中声明的成员),取决于基类成员的类型, 与派生方式无关。可归结为下表: 基类成员类型
私有成员 保护成员 公有成员 • 示例程序:11_2
私有派生
否 是 是
公有派生
否 是 是
派生方式影响什么?
• 派生方式决定:派生类继承于基类的成员的安全类型。 – 私有派生.基类的公有成员和保护成员继承到派生 类后,成为派生类的私有成员. – 公有派生.公有派生不改变继承成员的安全类型。 • 通过如下方式可以改变继承成员的安全属性。 class A{ public int x, y; void f1(); }; class B: A{ public A::x; //注意:前面不要写int. A::f1;}; // x,f1都为public类型 • 示例程序: 11_3。
输出: B(). A(). C(). ~C(). ~A(). ~B().
虚拟继承和虚基类
class x{ protected: int a; public: x() { a = 10;} }; class x1: public x{ public: x1() { cout << a << endl;} }; class x2: public x{ public: x2() { cout << a << endl;} }; class y: x1, x2{ public: y() { cout << a << endl;} // error. x1::a或x2::a };
虚继承的初始化顺序
• 在多继承时,使用虚继承的基类与使用非虚继 承的基类初始化顺序不同:
– 虚继承的构造函数在非虚继承的之前调用. – 若同一层次中包含多个虚继承,这些虚继承的构造 函数按他们说明的顺序调用. – 无论任何方式的继承,基类的构造函数在派生类 的构造函数之前调用.
• 示例程序: P207例程
3.派生类的构造函数与析构函数
• 派生类构造函数执行之前,自动调用基类的构造函数. 派生类析构函数执行之后,自动调用基类析构函数.
class A{ public:Байду номын сангаас
A() { ~A(){ class B: public A{ public: B() { ~B(){ main(){ B b; } 输出: printf(“\nconstruct A.”); } printf(“\ndestroy A.”); } };
class A{protected: x; public: y;}; class B: public A{ public: void f(){ A a; a.x = 0; // 是否可访问a.x ? a.y = 0; // 是否可访问a.y ? this->x = 0; // 是否可使用A中声明的x ? this->y = 0; // 是否可使用A中声明的y ? } };
class string{ //… // 字符串类,教材P166 }; class edit_string{//… // 可编辑字符串类,教材P166 };
• 通过引入继承:
class edit_string: public string{ // 可编辑字符串类 public: edit_string(); ~edit_string(); int get_cursor_pos(); void get_cursor_pos(int how_much); int add_at_cursor(char* new_text); int replace_at_cursor(char* new_text); int delete_at_cursor(int how_much); };
虚拟继承和虚基类
• 有关多继承,虚拟继承和虚基类等知识的详细 说明,请参考《C++ Primer》(E3)或 《Thinking in C++》(E2)中相关章节。 • 多继承在成员使用及向上映射时容易引发二 义性问题,应尽量避免使用。 • 虚拟继承比非虚拟继承开销大,也应尽量避 免使用。 • 示例程序:11_6
5. 继承举例
• 示例程序11_7: 给定一个线性表类list,分别派生出栈类 stack和队列类queue.
– 线性表:元素依次呈线性结构(链表,数组都是线性表) – 栈: 操作受限的线性表,只能对栈顶的元素进行操作. – 队列: 操作受限的线性表.删除时只能删除队头;插入时只能插 入到队尾.
派生类的定义
• 派生类的定义有两种:
– 私有派生 – 共有派生 class x{ // … }; class y: x{ // 私有派生 // … }; class y: public x{ // 共有派生 // … };
派生类
• 当继承基类时,派生类就获得了基类所有数 据成员的副本.
class A{
派生类对基类成员的访问权
• 派生类是不同于基类的新类,他对基类成员的访问权 取决于基类成员类型,与派生方式无关。 • 派生类对基类成员的访问权可归结如下表: 基类成员类型
私有成员
私有派生
否
公有派生
否
保护成员
公有成员
否
是
否
是
从上表可看出,派生类对基类成员的访问权,与其他 任何类对基类的访问权一样。
• 示例程序:11_1
• 教材P171图表述不准确. • 示例程序:11_4, 11_5(P171例程)
派生类域:同名变量和函数
• 派生类域被自动嵌套在基类类域中.
class base{ protected: int x; public: base(){ x = 0; inc(); } void inc(){ ++x; } void display(){ cout << “base: x = “ << x << endl; } }; class derive: public base{ protected: int x; public: derive(){ x = 10; inc(); base::inc(); } void inc(){ x += 10; } void display(){ cout << “derive: x = “ << x << endl; } }; main(){ 输出: 注: base b; derive d; base: x = 1 sizeof(base)=4 b.display(); derive: x = 20 sizeof(derive)=8 d. display(); d.base::display(); } base: x = 2