C++ note (9)
            
            
                 
                    继承和派生 Ⅱ
派生与构造函数
类不可继承的成员
类不可继承的成员有
- 私有成员
- 构造函数与析构函数1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15class MyString : public std::string { 
 public:
 MyString(const char* s): string(s) {};
 };
 
 int main() {
 //ex1: 编译,并观看编译日志
 MyString str1("继承 string(const *char)");
 str1 = "继承 string::operator=(...)";
 cout << str1 << endl;
 MyString str2 = "hello ";
 cout << str2 + str1 << endl;
 //ex2: 取消第 6 行注释,编译并运行
 return 0;
 }
向基类构造函数传递实参
若基类构造函数带参数,则定义派生类构造函数时,仅能通过初始化列表显式调用基类构造函数,并向基类构造函数传递实参。
带初始化列表的派生类构造函数的一般形式如下
| 1 | 派生类名(形参表) : 基类名(实参表) { | 
举例:time类
| 1 | // SPECIFICATION FILE (time.h) | 
| 1 | // SPECIFICATION FILE ( extTime.h) | 
| 1 | // IMPLEMENTATION FILE (extTime.cpp) | 
- 初始化器列表中写 zone(initZone) 更显 C++风格
- 传递给基类构造函数
- 基类构造函数在派生类构造函数之前调用1 
 2
 3
 4
 5//base class default constructor is called prior to the derived //class default constructor. 
 extTime::extTime() { // 编译器默认添加 `:time()` 初始化基类成员
 zone = EST; // 如果枚举值标识符崇明,使用类型::枚举值。例如:zoneType::EST
 }1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13void extTime::set( int hours, int minutes, int seconds, zoneType timeZone) { 
 time::set(hours, minutes, seconds); //调用基类函数。Why?
 zone = timeZone;
 }
 void extTime::write() const
 {
 static string zoneString[8] ={
 "EST", "CST", "MST", "PST", "EDT", "CDT", "MDT", "PDT"
 }; // 数组比switch语句好
 time::write();
 cout << ' ' << zoneString[zone];
 }
派生与成员函数
概念
- 重载(overload) - 具有相同的作用域(即同一个类定义中);
- 函数名字相同
- 参数类型(包括const 指针或引用) ,顺序 或 数目不同
 
- 覆盖(override)- 修改基类函数定义 - (记得加链接) 
- 隐藏(overwrite)- 屏蔽基类的函数定义 - 派生类的函数与基类的函数同名,但是参数列表有所差异。
- 派生类的函数与基类的函数同名,参数列表也相同,但是基类函数没有 virtual关键字。
 
- 继承(inheritance) - 没有被覆盖或隐藏的基类函数,包括在基类中重载的函数
 
重载和隐藏的区别
例如
- time::set(int, int, int)
- exTime::set(int, int, int)注: int, int, const int 算不同的参数类型 ownership 还在函数调用点外部 int 和 const int 算相同的参数类型 传参已经 copy,不再管 constant modifier 
如果 set 在一个类中定义,则是重载
set 的签名不一样
如果同名函数出现在 baseClass 和 derivedClass 中,且满足隐藏的特征 1 或 2
- exTime 案例 set 满足特征 1
- exTime 案例 write 满足特征 2
 所以它们都属于隐藏
隐藏的应用
- 利用隐藏,实现在派生类中修改成员函数的功能,如 write
- 利用隐藏,赋予派生类成员函数新的功能1 
 2
 3
 4
 5
 6// IMPLEMENTATION FILE ( time.cpp ) 
 void time::set( int hours, int minutes, int seconds) {
 hrs = hours;
 mins = minutes;
 secs = seconds;
 }1 
 2
 3
 4
 5// IMPLEMENTATION FILE ( extTime.cpp ) 
 void extTime::set( int hours, int minutes, int seconds, zoneType timeZone) {
 time::set(hours, minutes, seconds);
 zone = timeZone;
 }1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13// base.h 
 class base {
 public:
 // 没有默认构造
 base(int p1, int p2);
 int inc1();
 int inc2(); // 被继承
 void display();
 private:
 int mem1, mem2;
 };1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 // derived.h
 class derived : public base {
 public:
 derived(int x1, int x2, int x3, int x4, int x5);
 int inc1();
 int inc3(); // 新添成员
 void display(); // 隐藏规则 2, 修改定义
 
 private:
 int mem3;
 base mem4; // 类成员,注意初始化方法
 }1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 base::base(int p1, int p2) {
 mem1 = p1;
 mem2 = p2;
 }
 int base::inc1() {
 return ++mem1;
 }
 int base::inc2() {
 return ++mem2;
 }
 void base::display() {
 cout << "mem1 = " << mem1 << ", mem2 = " << mem2 << endl;
 }1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 derived::derived(int x1, int x2, int x3, int x4, int x5) : base(x1, x2), mem4(x3, x4) { // 基类、类成员初始化
 mem3 = x5; // 基类、类成员 不能在这里初始化!
 }
 int derived::inc1() {
 return base::inc1();
 }
 int derived::inc3() {
 return ++mem3;
 }
 void derived::display() {
 base::display(); // 被隐藏函数成员调用
 mem4.display();
 cout << "mem3 = " << mem3 << endl;
 }1 
 2
 3
 4
 5
 6
 7
 int main() {
 derived obj(17, 18, 1, 2, -5);
 obj.inc1();
 obj.display();
 return (0);
 }
存储结构
(todo)
改变访问控制
恢复访问控制方式
基类中的 public 或 protected 成员,因使用 protected 或 private 继承访问控制而导致在派生类中的访问方式发生改变,可以使用“访问声明”恢复为原来的访问控制方式
访问声明的形式
| 1 | using 基类名::成员名;(放于适当的成员访问控制后) | 
使用情景
- 在派生类中希望大多数继承成员为 protected 或 private,只有少数希望保持为基类原来的访问控制方式1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25class base { 
 public:
 void set_i(int x) {
 i = x;
 }
 int get_i() {
 return i;
 }
 protected:
 int i;
 };
 class derived : private base {
 public:
 using base::set_i;
 using base::i;
 void set_j(int x) {
 j = x;
 }
 int get_ij() {
 return i + j;
 }
 protected:
 int j;
 };1 
 2
 3
 4
 5
 6
 7int main() { 
 derived obj; // 声明一个派生类的对象
 obj.set_i(5); // set_i()已从private转为public
 obj.set_j(7);
 cout << obj.get_ij() << endl;
 return (0);
 }
屏蔽基类成员
目的:
- 使得客户代码通过派生类对象不能访问继承成员。
方法:
- 使用继承访问控制protected和private(真正屏蔽)
- 在派生类中成员访问控制 protected 或 private 之后,使用 “using 基类名::成员名”(非真正屏蔽,仍可通过使用“基类名::成员名”访问)
用于继承对象的重命名
目的:
- 解决名字冲突。
- 在派生类中选择更合适的术语命名继承成员。
方法
- 在派生类中定义新的函数,该函数调用旧函数;屏蔽旧函数。
- 在派生类中定义新的函数,该函数的函数体与旧函数相同。
使用基类构造函数
目的:
- 使得派生类对象直接使用基类的构造函数。
方法:
- 在派生类中使用 using 基类名::基类名
继承机制的应用举例-图形的处理
将圆看作是一种带有半径的点,将点看作是一种带有显示状态的位置
| 1 | //说明:类location以x和y坐标描述了计算机屏幕上的一个位置。 | 
| 1 | 
 | 
| 1 | 
 | 
| 1 | 
 | 
继承对象的重定义
派生类中修改继承成员函数的语义(即,修改函数体,而保持函数原型不变)
派生类中的名字屏蔽基类中的名字
| 1 | // 说明:类point描述了某一个位置是隐藏的还是显示的。 | 
| 1 | void point::show() { | 
| 1 | 
 |