o9 N# V& [ v2 E& D y9 n( D
继承和派生的概念: _+ A g( ~2 W; Z: V" f* O2 d
+ J, b( G5 s& e, z8 ]% A/ k, e01派生
7 K. Z7 p0 Z7 k; c
" K$ H/ G0 B# H8 B
通过特殊化已有的类来建立新类的过程,叫做“类的派生”, 原有的类叫做”基类”,新建立的类叫做“派生类”。 3 W2 M8 Q. j: }4 g1 u
02继承
0 x! k$ N3 c' M. Y& G5 q
H! e1 p( F- O& B: s( w2 l' N% Z
类的继承是指派生类继承基类的数据成员和成员函数。继承用来表示类属关系,不能将继承理解为构成关系。
; Q/ a: \0 M" b" @. o& _03继承派生的作用
0 J! f( {% ^. q9 m6 Y1 ~' W1 P" X# \
增加新的成员(数据成员和成员函数)
& c) R1 X% [5 z# A
重新定义已有的成员函数
$ M% G- @! U @, b! H9 B
改变基类成员的访问权限 1 [6 G$ R$ C1 C: [. y4 Q
5 u2 u3 E% E2 Y7 q
单一继承
$ ]# }: {1 j) c& T) m% v* n4 m5 h1 n1 H; u- _" K
01一般形式
" {% T. h) y# w9 c4 g2 d F; o0 v
代码格式:
3 q# Z8 d& l3 \- class 派生类名: 访问控制 基类名 { 2 z3 r x/ \# u6 U% Q: I- K7 m! z
- private: 成员声明列表 6 B( `, {. A: b$ t0 v1 M, G
- protected: 成员声明列表 4 z$ S$ C4 X% U
- public: 成员声明列表
( } u: r5 F. H9 q7 n6 Q - }
复制代码 ) b `$ p2 f* ~' |& Z: F3 |
“冒号”表示新类是哪个基类的派生类;“访问控制”指继承方式。 三个方式:public、protected、private
6 r8 Q& P& E3 G$ E& l I: l02派生类的构造函数和析构函数( L! G7 K G+ A: {
l0 W5 j/ k& N/ N4 M- // 基类
1 f: D) [9 l3 ~2 E, L: |: k - class Point {
$ {4 f! Q7 \2 x0 U+ }$ z7 A, _1 b7 E - int x;/ o9 j& a& [/ T( I! k. k2 {
- int y;' p* w( I6 b9 [/ B! X& @5 J
-
9 U# s! w, b, ^. H q v7 }0 k( h - public:2 z% K, O" |/ s( Y9 F& _
- Point(int a, int b) {
; O* m: E v; k - x = a;8 `1 Y" r9 _' W$ L* w
- y = b;) l8 @6 D1 @+ R V; B" b3 T4 u
- cout << "init Point" << endl;. N* @- \! W7 k9 n2 M$ D
- }
1 S9 t: X1 u, R' I/ Q; ]$ }+ t - void showPoint() {
. T8 l! d: H. |3 x" Y8 q6 p0 r - cout << "x = " << x << ", y = " << y << endl;0 v3 c) h/ |% I& p2 w7 n% V
- }2 J- I8 R' O+ L% v( l ], t* [, ?8 b
- ~Point() {
: K8 O3 x% T. j/ H+ @) {, c1 P - cout << "delete Point" << endl;
7 p/ Q9 N# j6 L& A1 E# n3 h - }& v. m \+ d; B+ M
- };
7 U7 b0 J W# N
3 z0 ^! p0 @& ]( Q8 n- C+ C- // 派生类( V$ t( M& j: y% w4 d4 I2 ~
- class Rect: public Point {8 y3 u8 ~' ~; c, P I/ }8 ^, @8 H
- int w;1 p J9 \# E: K' P6 w! B
- int h;8 }7 J! d0 ?( o5 R' H
-
{4 _* s/ }1 y& n n$ a$ B k - public:
( p3 s2 Q6 C% y - // 调用基类的构造函数对基类成员进行初始化5 R4 \% ~5 x" I9 ^, G4 n
- Rect(int a, int b, int c, int d):Point(a, b) {
! @" y$ b4 _) s2 } - w = c;+ a9 {" U& L8 z6 W) Y
- h = d;, a1 f' r3 a" g! o: q; j& W5 Z
- cout << "init Rect" << endl;
1 ^6 b; \% x) Z% e; w - }( U7 r2 C a7 ^1 Y) a
- void showRect() {6 O, N: e! P' x( W! {) a
- cout << "w = " << w << ", h = " << h << endl;
; N3 e1 u# X$ {; h - }
; N2 X) H, r7 I1 j( z/ d9 g - ~Rect() {
* P3 Z' U; @+ d/ j - cout << "delete Rect" << endl;
" O: P/ C1 {% u. I, c/ c - }
9 n R. G p2 @2 H! d" r. f - };
1 i) J. U3 z! l0 l/ r
, u3 w* |/ |) J2 B- int main() {5 x! w8 _9 E. q7 U
- Rect r(3, 4, 5, 6);
- i( y2 d. I4 Q1 c S2 H - r.showPoint();
3 j7 r/ N, i( t7 p2 n/ X1 ^) z - r.showRect();
# c. [. H4 e- U5 o+ U* J: i$ J - ! n7 \9 X+ {8 F
- /** 输出结果
/ @4 l$ [% }1 u- G$ | - init Point // 当定义一个派生类的对象时, 首先调用基类的构造函数, 完成对基类成员的初始化
2 v1 ~% g' a0 s: D - init Rect // 然后执行派生类的构造函数, 完成对派生类成员的初始化# m U! U% F/ Q- D" D2 Q
- x = 3, y = 4 // 调用基类成员函数showPoint();8 p) \& U6 q2 f9 Q8 Z* k! n9 B- R
- w = 5, h = 6 // 调用派生类成员函数showRect();7 r, B& ~& n5 c. t
- delete Rect // 构造函数的执行顺序和构造函数的执行顺序相反, 首先调用派生类的析构函数: {' F" w- R9 y r. ~0 c
- delete Point // 其次调用基类的析构函数' g: x0 y5 }1 |* ~% ~
- */
9 n+ T9 \1 ~7 o) f b - }
复制代码 5 ~6 |2 o, d" {# e
03类的保护成员
! p+ `* G+ b3 X( f v9 }) @& K1 B% @- S! y, e& s) z+ F- S' h
如果希望Rect中的showRect()函数可以一次显示x、y、w、h。我们直接修改showRect()函数是不行的。
: v6 I, f0 M# S) s( o- void showRect() {
, o( p5 l; W# r+ X8 j0 w1 h5 m - cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;4 E# V9 G! M% `; z& C" H! x( A
- }
复制代码
+ y8 p6 p) Y m6 W2 ?9 h9 N报错 error: 'x' is a private member of‘Point' 'y' is a private member of‘Point' & W3 ?" o. J& D- ~
x, y为Point类的私有成员,公有派生时,在Rect类中是不可访问的。 / Q9 Y i) O' y5 @
我们还需要将基类Point中的两个成员声明为protected的属性。 ) o1 X/ A- c2 ]; D [
像这样:
9 E6 E4 A& ^0 e" P0 I- // 基类
7 p& T1 |; D* y; Q - class Point {3 M9 ]& e: P3 I/ R' N6 q, a
- // 公有数据成员3 X$ @ K5 m. h9 U4 F |
- protected:+ e! a9 n7 [- V* a& d v; ]
- int x;
7 J; r# N; f! D0 D: m& Q0 i5 t - int y;
$ V& l5 w; y- t! ^ h - 0 r# {# S: [% U
- public:- w2 B, n1 ]5 X; m+ L: u
- Point(int a, int b) {% _$ h N+ B( z+ L% y/ T; A
- x = a;
) u; i, \! S8 W$ ^* n: t - y = b;" g' M$ g, n8 G6 D7 W' t$ p( I
- cout << "init Point" << endl;4 D% D9 O9 N- G: i9 u, d
- }
1 r. ~, Y% Z& u - void showPoint() {
# |8 X* C1 u. W9 A - cout << "x = " << x << ", y = " << y << endl;) ]5 b5 |7 @1 q/ y8 U- L! }5 \, C
- }: i7 e& {) P! j& e1 s/ j- d
- };
- ~* K2 _2 g2 u8 B" w - / Z# Z# O6 s* s/ ~
- // 派生类
3 G+ k S3 h$ \) L% _ C" x - class Rect: public Point {7 q$ }/ @% M6 M; L" @! A
- int w;) i$ d: u2 J# I/ U9 V
- int h;
8 Q/ H8 _. b8 ?: Y) r e) c -
8 p" B' z6 {8 q! X0 s/ G - public:
# j) h5 e' M8 P+ B - // 调用基类的构造函数对基类成员进行初始化. ?7 I2 }/ [; z& i! C
- Rect(int a, int b, int c, int d):Point(a, b) {! p) r( H. K3 P# z3 ?
- w = c;6 B# v5 z3 D$ P. V; C
- h = d;
" s& G6 Z" Q, P' P! b; P - cout << "init Rect" << endl; v' m# Q [$ O
- }
9 P" S4 |6 t' x) \$ ]9 H - 4 P" J, z8 y0 y( l
- /** 公有派生, Point类中的受保护数据成员, 在Rect类中也是受保护的, 所以可以访问 // 而通过公有继承的基类私有的成员, 在派生类中是不可被访问的 void showRect() {3 f0 t v" D x; {) f3 k
- cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;
+ O6 ]3 }0 e+ k, z2 A* Y7 d - }*/
/ b/ G0 t4 h* `! r) `+ H - };
( F8 |. [% _5 b6 ^ - - o1 _* M$ L% i' ]: m. O+ X7 N
- int main() {2 r% s2 ?9 k( ]1 S! [% X
- Rect r(3, 4, 5, 6);
) ^0 Z( a! r' U, Y - r.showPoint();- t% A% w) q: {! L
- r.showRect();
8 O9 D4 t d: [. ]0 Q x, X4 u - }
复制代码 ' S+ J; a6 ^2 y' R9 E8 L% ^6 f4 Z
04访问权限和赋值兼容规则& r" B3 L& H" {1 Y7 p& ]9 r; a
C1 T% J( v6 S: b, P- g6 ^
在根类中,对于成员的访问级别有三种:public、protected、private
- k& n/ S1 R; n, V% Z
在派生类中,对于成员的访问级别有四种:public(公有)、protected(受保护)、private(私有)、inaccessible(不可访问)
6 z7 X5 @! t8 V: ], g9 B3 W3 v
(1)公有派生和赋值兼容规则 1 M3 A- y2 I- ~6 {$ ~: z; F
公有派生: # d5 ~ m! I4 w* M4 q5 _% z+ {
基类成员的访问权限在派生类中基本保持不变。 9 s h2 K$ _) w4 Q! r6 O, p
基类的公有成员在派生类中仍然是公有的 . T1 F) {* e( z( h
基类的保护成员在派生类中仍然是受保护的
7 F! ?. q8 N2 a/ {; X
基类的不可访问的成员在派生类中仍然是不可访问的
5 S6 Y" {" I9 z g9 m
基类的私有成员在派生类中变成了不可访问的 - B) S/ e) j) ]
; N2 s& L. x+ ]& _7 [0 p0 U
总结:在公有派生的情况下,通过派生类自己的成员函数可以访问继承过来的公有和保护成员, 但是不能访问继承来的私有成员, 因为继承过程的私有成员,变成了第四个级别,不可访问的。
) x ^2 C3 ]* t% g( J/ G% F* D; y% t
赋值兼容规则:
' s7 \2 W3 F* e1 E
在公有派生的情况下, 一个派生类的对象可以作为基类的对象来使用的情况。
( T, s+ T! U4 ^ r9 q% o
像这样:
$ A9 I& g3 t7 B* L7 ~3 Q- // 基类
]$ T6 i& ?! `8 a; ]' y - class Point {
+ E z5 P+ y: I6 z/ e8 @; y/ o - // 这里声明成员属性为受保护的1 r. z3 \6 {+ f4 h9 g7 n6 E
- protected:
, _ K+ S: q* K3 ? - int x;
) O( g! y' \* Z! ` - int y;8 ]0 t- F- F, Q1 D I3 A
-
5 k8 v' r! W1 \+ v: v, a5 o - public:
S1 w8 e5 p/ k' F4 G - Point(int a, int b) {
/ y9 U7 w- ~: _5 q) O2 _0 ^+ q - x = a;
; r. B) G$ e) k3 l& Y7 c - y = b;, e! {3 U9 S I0 p" \
- }
& ~& P5 N5 O* N8 M N -
! U/ d) G1 v* R2 c' y* y: ^ - void show() {1 j4 B- s8 D3 w. M& T: ?
- cout << "x = " << x << ", y = " << y << endl;
) |% ~. v) g4 X, Q: {( E( t4 C - }, ~8 I4 M7 A( [0 L
- };
* d& z1 p/ ~6 b) n2 @( x! F - " b3 _# ]( b/ V/ y1 `4 a0 Z+ H
- // 派生类
. \3 e/ X0 r! K' {/ X( J" l& p - class Rect: public Point {* V, Q- }: {+ E) s( `
- int w;
1 [5 Z% I( H; o( U: c0 S: t$ s2 i - int h;
. k9 M9 _9 w; q; P - 6 p9 l9 @: p0 R+ k' s4 ]* {
- public:. d4 N2 X3 n+ V9 c# Q6 Y( Q
- // 调用基类的构造函数对基类成员进行初始化" O" h- `! {, o5 M: l" p
- Rect(int a, int b, int c, int d):Point(a, b) {
. G6 }1 f7 p4 l- l7 K - w = c;: a, J' r4 J) j# \& D2 i3 v
- h = d;
9 V% b7 a( R% l* e* M6 a6 V - }
' D T. ~7 C9 m. R7 t -
1 p$ K) t3 ]3 [ - void show() {
* V' A( U, k9 `4 }* t4 M - cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;" f, e# M# P: u) Z! Y3 o
- }3 m2 o5 v" K& U
- };
; G. K" P, e. R; g - 2 l/ o: y2 O" i- W9 `+ c7 o
- int main() { W% z4 P4 s$ R/ m2 @" @; ^$ A
- Point a(1, 2);- h9 ~5 }6 x- i& r$ M
- Rect b(3, 4, 5, 6);# U' |' C* J. @$ j) u/ s+ }7 d( d
- a.show();8 q* |2 F1 X" a% w
- b.show();
9 w* G6 {3 k" C _( E. ?" _& R9 g -
; e; v$ H- O7 i1 h5 {# P - Point & pa = b; // 派生类对象初始化基类的引用
0 s. {- L, C Z3 V9 \9 K# E3 i - pa.show(); // 实际调用基类的show()函数) s' \8 k) x: j/ [; o. c
-
6 p# M/ o5 j% V7 Z2 U$ w - Point * p = &b; // 派生类对象的地址赋值给指向基类的指针& `) S8 K n1 F3 {4 s0 Q
- p -> show(); // 实际也是调用基类的show()函数
7 C/ U1 h# W! c7 ]2 n8 [$ R - 4 F1 y6 M' t3 X/ ?8 R9 \) L z |2 }
- Rect * pb = &b; // 派生类指针
$ m9 l2 J4 }5 W4 p; ^7 V; i. } - pb -> show(); // 调用派生类的show()函数
3 n3 r( L' O' s& m2 r) @. \! } - ) V5 k4 {# O2 U2 b3 a& u# h0 _& P
- a = b; // 派生类对象的属性值, 更新基类对象的属性值! p- N' g0 i ~- N6 R. @: k
- a.show(); // 调用基类的show()函数6 q8 g( s& x6 b- ?) M H
- /**. n/ C, o5 R, e v/ y3 N
- x = 1, y = 2, d* O) y. @8 Q4 }. l0 i8 f2 ^
- x = 3, y = 4, w = 5, h = 6+ g% [$ ^0 k: d
- x = 3, y = 4
* ?/ I& p {$ \4 |4 u/ V - x = 3, y = 4
8 T) p" h) E9 c# ?" k+ ]8 R - x = 3, y = 4, w = 5, h = 6; b# h7 A* C% i; ?9 i5 a* D% z/ V
- x = 3, y = 4) ~1 C; c7 ^5 v7 t$ l0 R
- */3 ` r$ N" U8 |8 j; W' U
- }
复制代码
) s2 t7 a- t$ c$ Z# D5 |
(2)“isa”和”has-a“的区别
/ @" i. t" _- V7 Q, p
继承和派生 isa
( c2 n& o H0 \% Z
比如一个Person类,派生出一个Student类,我们可以说Student就是Person,也就是 Student isa Person,而反过来则不行。 ' }% v9 x& f$ z* g- R- X
一个类用另一个类的对象作为自己的数据成员或者成员函数的参数 has-a。
, ~( r* W0 M( t" U% m6 I. v
像这样:
3 o% G z: h# T" H7 O; J- // 地址类
1 M" Z i% d% A - class Address {};6 S' S& ]) s& G @/ ~' k
- class PhoneNumber {};: r7 i, s! h2 |' }
) }7 U6 t8 Y9 h9 u' `! I r- // 职工类
* g9 b w* p; x) i' @% s - class Worker {; j& k4 S: D# r8 L
- String name;( R# u) V3 Q9 E, t; b! n1 J
- Address address;
7 W- o% g4 P( A& e. H9 X; a2 B - PhoneNumber voiceNumber;
M* s& o2 T! L3 @3 P0 p" N( K - };
复制代码 4 D4 z4 c1 ?: d( B% a
表示一个Worker对象有一个名字,一个地址,一个电话号码,has-a的关系,包含的关系。
5 E- X! t4 C7 _2 ?
(3)私有派生
4 w' ~3 r$ R u5 Q
通过私有派生,基类的私有和不可访问成员在派生类中是不可访问的,而公有和保护成员这里就成了派生类的私有成员。 - " f6 `: F, H m# E2 f X J) ^$ t
- // 基类
# O4 S: _* r7 @" g/ s- B/ [ - class Point {% A" D& H2 `0 L' V- H7 } [
- int x;/ |; r; C5 O) n! z
- int y;
J9 c/ @. `1 H - 4 G# y8 _3 k+ d: Q' o. E5 R
- public:8 Q o5 x9 w7 ^1 v
- Point(int a, int b) {
; { U5 d: v( K) m, e7 F - x = a;9 T# c; B9 J0 e# f
- y = b;6 }, g0 O1 _% ?
- }
9 k4 n1 X; N/ s% Y - 9 E* e- P5 t) O: V Z
- void show() {
( L0 N6 w/ z! n' M/ Z6 p - cout << "x = " << x << ", y = " << y << endl;' z% W' ]/ Q" C+ ]# a$ ~9 V
- }% P# I7 T" T- t3 N) o) X
- };
H ?( H& S, c8 q& D
4 ~) i, R9 h$ u2 k' F) l/ d- // 派生类
6 n4 s" @" v% b5 K - class Rect: private Point {* D5 _; r, H6 p
- int w;
8 `/ `; z# k7 Y! r2 b0 } - int h;
0 r+ N ~8 c1 k- L9 p- V$ H - 5 {# _. A( o8 Q
- public:( h5 S, _2 C- Q# k$ z
- Rect(int a, int b, int c, int d):Point(a, b) {
3 J+ P5 y. |( j* G U2 Z - w = c;6 ?0 l$ c2 z. q$ T5 B* P
- h = d; e6 B7 Q" ^: \& R5 ^/ ]
- }/ {0 O3 O3 D* D$ \ G
- & H8 Y3 A* j, h$ {4 l6 C
- void show() {
0 f& R+ w% n5 b3 ]2 r) P - Point::show(); // 通过私有继承, Point类中的公有成员show(), 在Rect中为私有1 |6 v/ @' {% D3 N
- cout << "w = " << w << ", h = " << h << endl;; I+ D/ j: g. c8 l9 N
- }/ N4 S4 W& G0 F) J( A, W
- };
. e. q; f" h4 u# I
/ M! g4 {2 z3 \& T, b! y- class Test: public Rect {
8 y) \3 E2 v3 }. @. q% d -
! L n, Q: t9 B& ? - public:
7 y1 {8 k+ Q- n& b - Test(int a, int b, int c, int d):Rect(a, b, c, d) {9 y6 o* K9 Y5 @% S
-
a9 ^$ Y R j2 z& i6 C) d - }- L1 f0 ?9 J. n- ]4 p7 s( r7 z1 t
- void show() {
& {7 ^9 i& {. w d6 a- n - Rect::show();
0 J, Z* x: _( g7 c4 y, B0 p' S9 G" v - //Point::show();! W* u8 i0 M* J3 y5 y9 x% B
- /** error: 'Point' is a private member of ‘Point’1 W+ ^: V( g8 s8 B/ Y" W
- 标明: 不可访问基类Point中的成员" e: o' A1 [4 N8 @1 a1 J
- Rect类私有继承自Point类, 所以Point中的私有成员x, 私有成员y, 在Rect类中为不可访问: Point类中公有成员show(), 在Rect中变私有, ~6 J- K$ q' Q
- Test类公有继承自Rect类, 所以在Rect中成员x, 成员y, 仍然是不可访问, Rect::show()还是public, 但是Point::show()不可访问 */
" ^3 Q' a. M% t/ M b. `8 b2 S. g - }
& ~# ]2 }8 v1 X - };
复制代码 ! i9 E# ]' }/ s( [
因为私有派生不利于进一步派生, 因而实际中私有派生用得并不多。 " O+ M$ B- }/ {& E5 i3 _2 s
(4)保护派生保护派生使原来的权限都降一级使用 3 k4 G* f6 W4 O, D! d$ z
即private变为不可访问,protected变为private,public变为protected。
2 Q N' G' W: c
限制了数据成员和成员函数的访问权限,因此在实际中保护派生用得也不多。
3 S) w+ E. D- c% G
比如:我们在上个例子中,Rect类保护派生于Point,则在Test类中Point::show();就可以使用啦! $ [6 x t, v3 w
多重继承 b5 R. N" R* D( U0 D5 u" g& z6 @
' ^6 H+ U# ^5 n7 N01一个类从多个基类派生8 r4 H( c+ s% [( b) `7 r6 I _
" a' A: K" q# s, L7 R2 j0 L
代码格式:
1 m: y' f0 y2 ]2 |! h- class 派生类名: 访问控制 基类名1, 访问控制 基类名2, … {
/ M& c$ s( [& Z9 i0 j- w6 }- ~ - //定义派生类自己的成员
' N7 C' u8 Z4 B- \: o+ u - }
复制代码
. _$ D9 J+ W5 R0 P' P& y# n8 ^
像这样:
8 E* J- p1 y8 g& L3 x2 ?5 |: S% D- // 基类A, 也叫根类$ m+ L' a1 I3 a0 h/ \( I2 b- n2 X1 s
- class A {0 q# |) {* d1 _" ?4 k: R
- int a;$ p: x3 h& j, c7 T: c: r
-
% ?; ?# b7 w5 Q - public:9 q* X b/ i2 U4 V. F4 a6 w3 ~" S
- void setA(int x) {
( e9 |8 o4 M8 Z# \8 W8 B4 z8 p8 X- v - a = x;
% [* f5 S5 R& G, _ - }
% f/ w2 a$ i* z ~) K - 9 E% D0 g: }# `. P1 [$ H9 C
- void showA() {
& i1 p1 |2 v0 W/ B - cout << "a = " << a << endl;
; [& _6 b3 m& c& L1 x - }% V/ @( V9 P* S4 c" P
- };! l6 H" g; p% U# ~) f
( \# i4 T6 Y' O- // 基类B, 也叫根类
+ X' K7 X; C9 C. T h* A - class B {
' I$ Y& W8 C/ V7 i* g% t - int b;9 k) O* Y$ _9 T
-
2 P0 z c" Y) X& ^: m - public:' r+ }) Z( s6 ^+ |" K
- void setB(int x) {0 Y* M0 o- x0 k. P e
- b = x;
' W5 L# p5 E8 i6 l) m - }) x5 {: f/ }/ j
- ' X; w/ p* r& w
- void showB() {* r' g" t4 s) |9 b3 h0 o% S
- cout << "b = " << b << endl;
$ V3 d# H) Z7 Y" D5 Q( B - }- x3 Q9 N7 }! D3 s* O
- };
" ~4 Y0 a0 p1 [# F) X+ R2 ^/ \
* Z7 S- L) s) d" f4 u" q, H; b- // 多重继承, 公有继承自类A, 私有继承自类B3 s/ @/ f, }+ j- I
- class C: public A, private B {: x3 c8 Y& P) c
- int c;/ c8 `0 }% V/ o
-
k5 I# [7 ]" I - public:" b# t+ F1 y4 s! {) I' G! Z
- void setC(int x, int y) {
2 R' R7 b- `% b5 @, u' p - c = x;
% D6 j- E+ t% @% |. w& M - setB(y);
& L% f& p5 o5 T0 s1 b/ V - }4 {. P0 D, H/ ^' m9 q$ E
- # u4 M! t+ [8 p7 O$ { Q+ D5 A
- void showC() {8 E- g5 m( t" G0 y7 L" t M3 f' G
- showB();
. G) M! G: k+ d1 ^$ q* v% z g" V - cout << "c = " << c << endl;
! j5 e% h I+ m4 @9 u - }% }6 [4 U) ~$ J. e0 {
- };1 ^ U1 }8 a5 J4 z; s
: U, K' X7 T; P& B5 X6 H- @- int main() {! m. E; I8 i |: Q6 Q
- C c;
1 z% \4 ?5 e _ e1 `5 n3 Q - c.setA(53); // 调用基类setA()函数
6 H N; u) |* K' E% m0 z( x, { - c.showA(); // 调用基类showA()函数& s1 k* a. Z3 v* l& j1 [0 {/ W
- 7 [ n/ G$ @; ]" N
- c.setC(55, 58); // 调用派生类C的setC()函数
0 Y, E$ h i# _ - c.showC(); // 调用派生类C的showC()函数
+ L' d' U' B" ?: Y" F' O/ e - * `$ V2 h+ U7 R9 E" Y
- // 派生类C私有继承自基类B, 所以基类B中私有成员b, 在派生类C中不可访问, 基类B中公有成员setB(), showB()在派生类C中变私有. 在main()函数中不可访问
j/ o e! b% p# ^) j, L E, { - // c.setB(60); // error: 'setB' is a private member of 'B'
$ G% `3 o/ }' k% |; G0 _% z - // c.showB(); // 'showB' is a private member of 'B' ?9 h& H: @1 l* u3 f; \: L5 V5 a
- /**+ A1 h) E" {& e' c, k6 F- z
- a = 53
& y) n; c7 ^" B+ E - b = 582 A1 K8 Q6 x2 ^& T# `- z% T! F2 q
- c = 55/ y! G6 m, x, ~% i' S
- */1 A9 U% g, Q- p8 g2 T
- }
复制代码
: d! r1 H) o% R0 K, u二义性及其支配规则
/ j7 G; ?) c6 o; ]; J/ a$ C9 e5 R/ z6 {& D1 ?: T( E! c
对基类成员的访问必须是无二义性的,如果一个表达式的含义可以解释为可以访问多个基类中的成员,则这种对基类成员的访问就是不确定的,称这种访问具有二义性。 - C5 w' y0 H4 T% a4 m
01作用域分辨符和成员名限定3 r& Z, ?. f8 T; A, q$ @
* M- |2 Q' R+ u9 l+ ?( f
代码格式:
! g! r8 r; ?% H f2 M# I0 s! O( C- 类名::标识符
复制代码
: E4 a# l# f9 A _:: 为作用域分辨符,"类名"可以是任一基类或派生类名,“标识符”是该类中声明的任一成员名, 7 d# K0 e* B; `2 }. P5 ~" @ u5 j. B
像这样:
6 Y" Y* K+ n& ]3 K2 O% H: f4 W3 ~0 G- // 基类A, 也叫根类
0 l6 e& I5 X/ D. O/ v - class A {
, x$ J* s# T2 ^, S* x% v - public:
" y6 K- F( d$ @" J( X - void func() {7 j- \$ C& C. k+ u# H3 q" Z
- cout << "A func" << endl;
+ x/ y* }3 A: u a; L - }: G! r. Z4 G( v' n4 [; j0 H Q& v3 C* o
- };% J" U1 O; `% d( u: K+ O! M
- / p2 r U! `3 Z' J; f
- // 基类B, 也叫根类
9 S/ E6 ~' V- J7 i6 j/ C, E! {; V - class B {$ o: \" b2 a- L. c& Y7 p v
- public:$ `" k4 Q1 }- C" L4 H
- void func() {
+ E" m$ |, V. ^" l2 B2 ~. U - cout << "B func" << endl;
3 J: l7 J7 f# @# ?9 ]( M - }) }0 Y6 B @9 n' q
-
" ?/ E, C6 W1 j( t - void gunc() {
5 e) m) h) e( D& m- S! t+ t - cout << "B gunc" << endl;; h: k+ o( c% B5 p5 J3 L
- }
* L& E: G; S5 H9 v9 p b - };
% V6 j! F( o. }: r- W7 W
+ s" j/ N2 Y& m5 M O# s8 C- // 多重继承
; n0 N+ ]8 g* _ - class C: public A, public B {
7 l# t% J: t# C3 h1 B - public:# y! k0 t1 O3 a/ o- k
- void gunc() {6 T1 ~/ M0 q4 @0 x* G
- cout << "C gunc" << endl;
! g" J+ n9 o; R+ F- h' U9 _ - }
( P# `) ^" _# z: [ -
! v' r- b, O4 w; o - void hunc() {
; y, M1 \8 B) A& f - /**
4 z0 c( A7 @5 h8 u* d - 这里就具有二义性, 它即可以访问A类中的func(), 也可以访问类B中的func()
" h9 t( F1 k5 A2 _9 t. w - */
/ S% ], ]; S. s+ t) e+ x3 v, F - //func(); // error: Member 'func' found in multiple base classes of different types
- B& @; q, B* P& R9 j, O; U - }
3 u9 Z* x8 t( [/ ^ - & ~8 E* q; T& o8 n+ t
- void hunc1() {
7 @( H: A, J+ M) C7 b - A::func();
4 V& R4 h$ J% U5 c- H1 B0 t - }
# } ?# y, X: [& ^9 ] - " ?3 v/ E8 L4 e# z, m# @
- void hunc2() {( D6 W3 Z8 d9 ?" H) V4 u
- B::func();
: o) i0 G/ ~" @) Y1 z - }% v% j. K. V" `: g8 o
- };
8 \. a: d: o: B Y; W9 ^% o
* o1 @- Z: E5 O: ]! \1 B* N5 M1 u- int main() { `: l7 d" k& H+ `' {- A% @
- C c;
% a+ P) A! C# f7 e3 T& P# q8 P - //c.func(); //具有二义性
0 I* }5 O! o5 J# |# q- f7 R5 Q. U - c.A::func();: o% A2 ~- x2 j* l4 z
- c.B::func();
! b' b: ^# o& u; }* _& s O" Q2 R - c.B::gunc();, v! }- c; |/ z; T1 k: D3 P. H* P( [
- c.C::gunc();% y' L4 v0 \& z) Z# ~, h
- ) M, X- s, Q; d, y! ]) v) C
- c.gunc();
. o" z+ g4 r2 T- g5 T - c.hunc1();) _! x# a+ p4 g6 H
- c.hunc2();
9 H7 `7 W2 R2 v$ B$ ~. V4 z - 0 n4 w$ I& u4 r+ v* `+ }
- /** 输出结果8 S% R) W4 b8 R7 j
- A func
$ z" J/ z# c! F, ^8 M. t, [ - B func: o; n3 v" q8 W" H4 M4 s
- B gunc
5 \6 l' y: N' y+ r7 H: L# d' [# f" h - C gunc" L6 n0 b- d- |5 w) d3 [
- 5 O9 s2 T' r) v3 F. h( H
- C gunc // 如果基类中的名字在派生类中再次声明, 则基类中的名字就被隐藏. 如果我们想要访问被隐藏的基类中的成员则使用作用域分辨符B::gunc();. r% z0 D5 L. F4 m+ a
- A func
7 p$ r0 _ j2 l+ r - B func% J, e" R! d, K: L7 R
- */
, d# q" g" ^# w/ }# {. i - }
复制代码 7 y ]) {$ V& j
02派生类支配基类的同名函数5 `+ s( u; ~8 J( P6 d( i! ]/ e0 ~
* {+ C- I: J6 l5 R) G
如果派生类定义了一个同基类成员函数同名的新成员函数(具有相同参数表的成员函数),派生类的新成员函数就覆盖了基类的同名成员函数。
' ~. _" U$ b: k8 J! R
在这里,直接使用成员名只能访问派生类中的成员函数,使用作用域运算符,才能访问基类的同名成员函数。 4 k4 r# @+ Y, a# L: F
派生类中的成员函数名支配基类中的同名的成员函数名,这称为名字支配规则。
9 @8 j1 c8 B6 ?& A7 @. m* ?! R* t( Y- R
如果一个名字支配另一个名字,则二者之间不存在二义性,当选择该名字时,使用支配者的名字。
+ P. q4 }" v; Z8 X A* ~7 u
例如上个例子中 - # G# h# K$ x% B
- c.gunc() // 输出”C gunc”, 基类B中的gunc成员函数被支配了
; N' {% f+ Q1 w, u: B - c.B::gunc(); // 加上作用域分辨符, 来使用被支配的成员
复制代码 # l9 V: \! n9 Z# s1 c g, ?+ G
总结
6 h, _2 C. B) h; O6 P6 w9 g
, z0 P" }) B0 V( `0 k9 U
C++中的多重继承可能更灵活, 并且支持三种派生方式。 我们在学习一门语言的时候, 更应该把精力放在它的特性上面, 不应该用什么语言, 都用自己所擅长语言的思考方式, 实现方式等, 要学会发挥该语言的优势所在。 # f3 P( H% H% S9 M5 h
|