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