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 |
|