你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C++——继承与派生

[复制链接]
gaosmile 发布时间:2020-9-7 14:16

& 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
   代码格式:

  1. : i# y4 k7 K1 T8 `) ]. [) s3 v6 c. X
  2. class 派生类名: 访问控制 基类名 {
    ( M1 a  L. J6 `' C
  3.         private: 成员声明列表
    ( n/ X9 B, t. X  c. s8 E
  4.         protected: 成员声明列表
    ( E2 X4 d# X9 d/ l( H2 O2 W
  5.         public: 成员声明列表: h. r. \* A  y
  6. }
复制代码

: {: 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
  1. : u1 r  l% G& g% r4 M- U
  2. // 基类
    ! x$ A/ F$ p4 h
  3. class Point {9 N* r; x+ J  W: R5 f* B
  4.     int x;
    6 Y0 w6 p: r4 s: W2 ?- b
  5.     int y;) X' x: g( e" R  F
  6.     + ~4 I, N  l" |- c$ p+ H( i5 [
  7.     public:
    ) v9 H+ c  r! ?& g2 R
  8.     Point(int a, int b) {7 P: z$ t7 f' ~, t- d
  9.         x = a;
    ) i; L, N) `" z
  10.         y = b;: S$ K  T: S7 ]% w; H% H
  11.         cout << "init Point" << endl;$ S( n$ W: t5 W- E0 n2 J
  12.     }
    % b) j1 w% X+ |% V5 u
  13.     void showPoint() {
    : `/ N& }/ w6 p8 z1 I2 ?0 y
  14.         cout << "x = " << x << ", y = " << y << endl;
    4 U$ b( x9 y) E2 U3 ^; L, }9 M
  15.     }! ]4 R' Q, y9 o, G9 m
  16.     ~Point() {: g* ^2 r; V" D/ {4 A
  17.         cout << "delete Point" << endl;
    6 X8 K& V' b* e5 R" a. a6 W
  18.     }8 q6 R: O4 C$ \: z6 w' o0 E
  19. };" Q* |/ Z5 x9 }1 n

  20. 4 V% s# q( s7 _
  21. // 派生类) K  d# A% h( @, m# T9 j
  22. class Rect: public Point {
      D1 f) Z# r+ f5 d/ r, X+ j
  23.     int w;
    5 f; E2 P; `3 n; V. {! C6 E
  24.     int h;
    ; c4 z2 b+ z- }% Q# n3 y
  25.     9 t: P+ g6 t  ]( h" G' p" p  H
  26.     public:
    $ \+ ^6 @4 Z* Z+ Q5 s3 f+ z
  27.     // 调用基类的构造函数对基类成员进行初始化# M/ W+ s# @4 i) i4 Z* ~, D5 v
  28.     Rect(int a, int b, int c, int d):Point(a, b) {7 s9 @2 t# m/ G( ^
  29.         w = c;
    . [' ?) Z; t% t& [8 b
  30.         h = d;
    , w# R, M9 B9 @& k5 L8 i( E* P+ x+ x
  31.         cout << "init Rect" << endl;
    . x/ [3 m5 W) y4 ^$ h, n; C
  32.     }
    8 {' H( G9 X* W" ?
  33.     void showRect() {
    ) E* \& s5 v3 r3 k( x8 H' K3 z
  34.         cout << "w = " << w << ", h = " << h << endl;
    7 w% e- w. r4 W( i3 n, ]2 h" |
  35.     }
    : b4 x' v. U* \& v" |
  36.     ~Rect() {
    # F4 Z9 z& s' C
  37.         cout << "delete Rect" << endl;: ^7 C% c5 L, v7 s
  38.     }3 E& o6 w$ G* \! o/ G9 K+ q
  39. };
    & w' y2 I) o, |8 R2 p5 t5 k+ x

  40.   g# z9 D) G! I
  41. int main() {
    ! U3 E6 u. o, x
  42.     Rect r(3, 4, 5, 6);
    5 H7 ~+ O* W" S9 ^! T
  43.     r.showPoint();
    ; Y' W4 R/ G1 n
  44.     r.showRect();
    ! }5 _* _$ ?1 J4 A' k! V: U9 i8 ~
  45.    
    3 Q0 H- N6 H& c3 D/ f! t. J' n
  46.     /** 输出结果
    - f% }& U5 I8 y4 ~6 g
  47.      init Point // 当定义一个派生类的对象时, 首先调用基类的构造函数, 完成对基类成员的初始化& \+ u' `( v, V# U  \9 t
  48.      init Rect // 然后执行派生类的构造函数, 完成对派生类成员的初始化
    6 M) o/ R; y1 Y" Y
  49.      x = 3, y = 4 // 调用基类成员函数showPoint();
    / s7 Q. M6 c) i9 B
  50.      w = 5, h = 6 // 调用派生类成员函数showRect();
    ( i# q: K' I- A; T0 U8 e- [# @( G9 N
  51.      delete Rect // 构造函数的执行顺序和构造函数的执行顺序相反, 首先调用派生类的析构函数
    : t  U% }  J% e- n( s2 h9 Q
  52.      delete Point // 其次调用基类的析构函数
    4 e9 }3 Z6 p' y1 @9 N
  53.      */
    & ]) d* P: B5 L- k; \8 R, l* G; e9 S
  54. }
复制代码

  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()函数是不行的。
  1. % W$ ~3 `" X( A6 Z
  2. void showRect() {" P6 \* d1 {. g) n& ]* _
  3.   cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;
    2 a  |, G+ {- s& h0 o! ~: y3 ]
  4. }
复制代码
$ 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
像这样:
  1. " |0 ?  r. C. f# V3 P
  2. // 基类- R$ w1 i" Z; D9 Z6 I$ P
  3. class Point {
    . d4 \  F, X/ _$ l4 f2 _
  4.     // 公有数据成员- k3 z- W) s4 s% E6 ^) N: l
  5.     protected:( y$ w) K/ B. j+ Z# F* g
  6.     int x;
    6 A, J1 z. Y% a
  7.     int y;( M2 U# _6 ~+ F: u7 U0 K. z
  8.    
    3 |( j; l5 c- ^! \3 u
  9.     public:
    + h* C: M# m7 d( [2 M. s
  10.     Point(int a, int b) {
    ! n$ I# S, ^) E+ F- g1 f. ?9 N; \: s
  11.         x = a;
    5 _0 z) r; B0 v7 v  X+ x
  12.         y = b;& ]$ X+ K' M4 D7 |1 Q+ O* L4 ^
  13.         cout << "init Point" << endl;
    - d0 M4 A5 [7 k5 P. s. B$ H
  14.     }4 n( M$ z: H! t2 |: K; c
  15.     void showPoint() {
    - o! W" h7 b% x
  16.         cout << "x = " << x << ", y = " << y << endl;
    , N9 X" M7 X/ [4 B
  17.     }( \. R. f% a7 U3 }' }0 T2 R$ L
  18. };
    ( M! s# u% j+ [. q8 ^# q

  19. ! Z" @, y& ]# M: Y
  20. // 派生类* R6 a! i) j' K" `; M& N
  21. class Rect: public Point {
    5 j; \# T1 N; c: Q4 w
  22.     int w;# A4 j" d9 x& g+ X- r, j' y
  23.     int h;9 ]/ B: o: Z+ M) L/ z' g7 g
  24.    
    ' A+ ]: p4 t- P- \+ l
  25.     public:
    $ p& K7 C& p3 h9 w7 _# I; V! i0 s- Q
  26.     // 调用基类的构造函数对基类成员进行初始化. t4 }! P% b9 Z" p% b- J- O
  27.     Rect(int a, int b, int c, int d):Point(a, b) {( R+ {  N+ p; I, x1 v" h
  28.         w = c;$ e' M. a, s' d; q* L: b0 f, a
  29.         h = d;5 z  v5 |% s# a& T, O
  30.         cout << "init Rect" << endl;: ?  q5 l* s, K1 x9 x
  31.     }
    8 `) G) Q6 k! R8 c: M  N
  32.     % d/ J( L: f. H4 x1 |; x
  33.     /** 公有派生, Point类中的受保护数据成员, 在Rect类中也是受保护的, 所以可以访问  // 而通过公有继承的基类私有的成员, 在派生类中是不可被访问的    void showRect() {
    7 q0 Q/ Q' v' Z# I, O
  34.         cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;
    + e* }1 O( ^, P  S: S" J6 v
  35.     }*/3 p5 P8 ?5 H5 i* O5 \$ T
  36. };& d, H; _7 D; Q1 O7 `
  37. ' t$ l  ]  e# s% x* d5 z1 D
  38. int main() {
    6 x: _9 N  g6 k+ G! Z2 Q
  39.     Rect r(3, 4, 5, 6);
    ! Y3 v" C* I2 t3 w* B
  40.     r.showPoint();
    $ I( s  Q  J3 M5 Y6 Q. Q
  41.     r.showRect();8 ~: m# I- [4 m& T5 D& q0 n
  42. }
复制代码

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
像这样:
  1. 9 q, g! `8 J# y/ H+ P
  2. // 基类8 H' z; P1 p: G$ ~  v/ A( B$ `
  3. class Point {
    ( F) ]( [+ y; J6 ^6 S2 p+ v
  4.     // 这里声明成员属性为受保护的
    & W+ u3 `  Y. ^& w/ J
  5.     protected:4 [) r$ c( r0 ~1 B0 t$ q' X
  6.     int x;4 Y" V& A3 @& z+ b$ }3 I$ Q
  7.     int y;3 F( x" L1 h. o, o, }
  8.    
    & ^2 g; e6 f' q( E8 D
  9.     public:
    ; Q  O. i1 r) L% d
  10.     Point(int a, int b) {. F4 b3 v' r( _( R* R3 C! A
  11.         x = a;
    * `4 Y/ O1 p" [# f  q
  12.         y = b;1 Z5 b  C; \8 e' g5 ?3 X% ^
  13.     }/ G3 Q3 a; O) o6 \
  14.    
    4 w) X, N+ @9 |) V4 w5 F2 ^
  15.     void show() {
    2 D4 g2 ?1 p3 [8 d8 v; F
  16.         cout << "x = " << x << ", y = " << y << endl;$ ~" f8 p5 k" ]( ?/ @- L) l
  17.     }
    & `" p0 p$ @6 _2 ^0 V3 T' s+ b, W) n8 I. @) j
  18. };& t6 s, d) i! I8 m; x

  19. 2 l6 Z1 j% Y& s! n% o# x3 r% L
  20. // 派生类
    : ]* A" d3 \5 }# R
  21. class Rect: public Point {5 S$ W$ k% j4 b$ f9 M# f6 p
  22.     int w;
    ; Y& N9 y. j/ H1 a
  23.     int h;! x/ M2 ]# z( B
  24.     5 j  O& U! W3 x. j
  25.     public:& I  B" \' q8 M2 X, j$ E2 [
  26.     // 调用基类的构造函数对基类成员进行初始化
    ' M% R4 }! m: E  A- U. Y& e
  27.     Rect(int a, int b, int c, int d):Point(a, b) {" v8 c5 e4 n* `$ d( E
  28.         w = c;
    . K: ]3 i' L+ E) O. z7 M, X2 o
  29.         h = d;
    + T  K9 n! [" a/ R* Y9 o! L
  30.     }
    $ y) t% j% D# N9 S  z
  31.    
    # t$ n# B; ^' c& R, D
  32.     void show() {( L0 T: U$ {/ G/ |3 R% X& d, R- }
  33.         cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;: J' l* a& a6 h; i  X( P
  34.     }
    % |0 F& D  R! S; f9 |: H9 k3 v
  35. };! U# G5 e3 W- @9 e0 a

  36. 3 u4 D: h( z; @2 E
  37. int main() {
    6 I. ?6 j% F/ Q
  38.     Point a(1, 2);# [; |3 X+ q+ |" o( v
  39.     Rect b(3, 4, 5, 6);! y# `& @' d* Q7 q8 E0 d
  40.     a.show();+ P5 h, v$ A" W" \
  41.     b.show();
    9 C" A" ~0 z0 B
  42.     ' s0 ]8 U7 e+ L
  43.     Point & pa = b; // 派生类对象初始化基类的引用
    0 N, m4 i8 n0 X2 f8 d" j1 c- d, o
  44.     pa.show(); // 实际调用基类的show()函数: T% Y, ]# B$ X# m( P
  45.    
    % S1 x; t" h0 _2 |
  46.     Point * p = &b; // 派生类对象的地址赋值给指向基类的指针
    % ~/ x3 }' [- X
  47.     p -> show(); // 实际也是调用基类的show()函数( m1 P" o, q1 [- Z! `
  48.     ( C3 s* D2 ?5 e" a" o2 H( T5 J
  49.     Rect * pb = &b; // 派生类指针/ I1 F! i2 p" S* \1 U
  50.     pb -> show(); // 调用派生类的show()函数
    / ]) n! r5 V6 Z0 C: `
  51.    
    & ?+ X9 W$ q$ K+ P: x, w1 d5 r
  52.     a = b; // 派生类对象的属性值, 更新基类对象的属性值  C6 a; @' V/ u( {* ^( Z
  53.     a.show(); // 调用基类的show()函数/ S  f6 E! S! t& s, B9 ?
  54.     /**
    ) X$ W; V6 V, i( \0 W  O3 q+ r
  55.      x = 1, y = 2* M$ z: a) v% U7 e$ v2 u6 z) _
  56.      x = 3, y = 4, w = 5, h = 6
    4 p0 v; K- X9 D2 b
  57.      x = 3, y = 4
    % v3 b" p' |0 i
  58.      x = 3, y = 4
    4 A8 b  L$ G. Z
  59.      x = 3, y = 4, w = 5, h = 68 g) t* G$ a% w( V6 B, C
  60.      x = 3, y = 4) l- ~4 C- i) a  s! Q
  61.      */! p: d- Y( h; A: B) S" w- W4 n1 T
  62. }
复制代码
, 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
像这样:
  1. : E; Z+ {6 K5 d5 g; A) _* r
  2. // 地址类: e# z6 W1 b/ O! }5 {+ T) T+ @( y$ P
  3. class Address {};
    9 ?4 D! I: Q6 U  Q3 R
  4. class PhoneNumber {};& e  J0 A% a1 u

  5. / C- S* I3 |9 z5 q! {
  6. // 职工类' \  y  {: i1 c0 m) ], ~4 g
  7. class Worker {
    6 t# {4 P% e& ?* ^0 _
  8.     String name;' h$ k6 J4 N6 A/ I
  9.     Address address;& e1 ?1 e2 Q. N
  10.     PhoneNumber voiceNumber;
    7 u  q, j# M; l# b6 f* T' T
  11. };
复制代码
+ O5 [6 G5 @. y+ D. D# U
表示一个Worker对象有一个名字,一个地址,一个电话号码,has-a的关系,包含的关系。

: k: E. G! F8 k6 @
(3)私有派生
7 E  P6 l/ V0 V3 T
通过私有派生,基类的私有和不可访问成员在派生类中是不可访问的,而公有和保护成员这里就成了派生类的私有成员。

  1. $ r  Y) b: {+ ~
  2. // 基类
    / V* @) g# j1 ^' A6 B  f* }/ y
  3. class Point {$ N7 F; x/ Y# h* C: w* i) v
  4.     int x;$ B( j! E- D& x& I
  5.     int y;
      b, W; u. }3 c: q8 |+ ]+ L
  6.    
    9 e# ~3 C/ U& s4 v6 W: s2 `
  7.     public:3 F. V/ J# i- b* `7 J
  8.     Point(int a, int b) {4 B* B1 Y/ C# s: |
  9.         x = a;
    . Q" l, ^0 I5 v6 k5 W. \+ c3 X
  10.         y = b;
    * d) e- C/ g* A
  11.     }; R% j/ n, l8 E: Z5 P7 h5 R
  12. ; W# n$ }6 X; s- n, P. P. c6 n: w
  13.     void show() {$ q6 {. ^% q$ e
  14.         cout << "x = " << x << ", y = " << y << endl;" ]( ?% k, Z( @) i3 n
  15.     }
    1 e" h( \* k' m% n
  16. };' |9 M! k& Q! Q0 T% t0 O

  17. 3 F' Z6 `( c0 C9 ?% p* u
  18. // 派生类
    ( r; S$ h4 _7 W  {6 W5 F" o1 w1 }7 M
  19. class Rect: private Point {5 S0 c- Z% U0 l+ h* Y9 r/ U
  20.     int w;
    4 @$ e! w& s# r5 p2 w
  21.     int h;3 p/ o9 j% Z) F1 d/ t
  22.     ( f( P7 G/ [6 n4 ?0 ?3 r/ s. D
  23.     public:/ w/ T8 w7 ~8 y
  24.     Rect(int a, int b, int c, int d):Point(a, b) {1 s0 X/ D5 I& j
  25.         w = c;
      [. y# N/ l. K! T' b6 n" G
  26.         h = d;# A2 u! B! S1 s
  27.     }
    1 f% o! O* o( F1 a" T
  28.    
    7 |% l- O6 R7 \2 `, X7 Z# `& l( L4 x
  29.     void show() {
    8 t8 Z: R3 a! ~
  30.         Point::show(); // 通过私有继承, Point类中的公有成员show(), 在Rect中为私有
      ^# M7 q- R. R% U- L3 A" _2 a
  31.         cout << "w = " << w << ", h = " << h << endl;
    " S1 n* g3 }5 P1 `
  32.     }
    + j& Q3 q' [1 H- V
  33. };+ H/ b$ O) c6 O4 W: D/ ^8 D6 r3 k7 i" g
  34. & v7 _, G( q0 x7 D1 L2 _  R
  35. class Test: public Rect {9 \  |: }/ n  A1 O) m
  36.    
    " n, i) g& D8 C2 t: u! Y
  37.     public:0 I6 k/ G1 y6 k
  38.     Test(int a, int b, int c, int d):Rect(a, b, c, d) {
    . a; W% l' l  g6 M) V- V2 i: C
  39.         3 d( [* s6 r  [  O  S
  40.     }
    + i; P8 U$ `2 N8 G+ w
  41.     void show() {  F8 [0 \- M( j
  42.         Rect::show();$ I6 w: K: d: L7 }0 f
  43.         //Point::show();1 Q/ v" S& I' N9 p
  44.         /** error: 'Point' is a private member of ‘Point’! e2 p. y+ @- d% n* Y
  45.          标明: 不可访问基类Point中的成员
    ( R* c9 [' ~% I. h$ J6 t5 I/ C1 ^
  46.          Rect类私有继承自Point类, 所以Point中的私有成员x, 私有成员y, 在Rect类中为不可访问: Point类中公有成员show(), 在Rect中变私有* F: M; `. A- X8 U
  47.          Test类公有继承自Rect类, 所以在Rect中成员x, 成员y, 仍然是不可访问, Rect::show()还是public, 但是Point::show()不可访问 */$ d2 d; K( w# s; S0 m2 ?5 @4 _
  48.     }
    5 G0 q  F0 @3 g" `* t& ?
  49. };
复制代码

* 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. 1 X5 i! i* Q- N9 U0 L
  2. class 派生类名: 访问控制 基类名1, 访问控制 基类名2, … {; l) i' o* E" o7 v% i% K7 E6 X- [2 _( X
  3.     //定义派生类自己的成员
    ; d0 ?/ L- N3 ?
  4. }
复制代码

) u0 L5 L2 g* d: C" |/ P
像这样:

  1. ) X2 O8 X4 W6 p9 N. U
  2. // 基类A, 也叫根类% ?0 I" J3 [/ ^/ [6 @4 b) J
  3. class A {! f6 f7 c% b" H+ G( L$ E
  4.     int a;
    9 v' }( S! M: [, C8 t, L7 Q+ |) r. I
  5.    
    % ]4 F5 Z4 b0 v& ~8 U  x
  6.     public:
    6 K1 ]% j5 }! ?: v! @* C1 l- V: q6 e
  7.     void setA(int x) {" i& o. f$ y" Q0 `
  8.         a = x;# S/ q- z6 g: |3 w6 n
  9.     }& g/ g3 k- R' b
  10.    
    ; B3 Y- n% t1 p  J" Z
  11.     void showA() {
    , r' k4 U  o- S7 s$ M6 T
  12.         cout << "a = " << a << endl;+ L7 R# L( h$ i; W
  13.     }
    & \; h8 r: F+ E+ {* i( s. h% R6 @
  14. };( E9 v, z8 R9 _. b9 K
  15. 4 P+ K3 A1 D6 w, l# ]
  16. // 基类B, 也叫根类
    $ P5 i6 f: Q# F- c# p
  17. class B {
    + g" O3 \1 f. K* Z$ U5 g
  18.     int b;
    1 x$ _% U2 h4 C9 |; Y. s+ U+ V0 g
  19.    
    $ L1 O! y3 V9 ]0 x6 R- @9 C
  20.     public:9 G  P8 y  @' P4 }
  21.     void setB(int x) {" i) E! x. }8 h9 I3 g; A
  22.         b = x;( l5 C/ _2 F2 h+ Q
  23.     }6 r& ~4 Q& R1 w% W
  24.     # ~( _2 e; S6 n& j) l( Y# `2 b
  25.     void showB() {, V/ j/ H" N/ _3 S) y- g
  26.         cout << "b = " << b << endl;
    ! c+ |) i! e+ ]( a; i4 d" d
  27.     }
    5 Z1 U2 l" _" v+ j
  28. };$ A$ ?* [  Z3 ^. e$ F) _, ?- D

  29. ! l+ H) h3 c- n5 o' \2 t$ g
  30. // 多重继承, 公有继承自类A, 私有继承自类B
    1 r! l; [1 ]+ L, H/ E- g7 [
  31. class C: public A, private B {
    ) y" z  a; _  z1 K. p# F) G
  32.     int c;
    " t9 l) D2 j3 \$ [
  33.    
    9 |& W' a: `2 T# z
  34.     public:
    $ [$ p# Z1 |* F5 @4 ~3 c/ W% W
  35.     void setC(int x, int y) {2 `5 k2 a# o' [
  36.         c = x;
    3 G( }3 X* }% Q8 Q
  37.         setB(y);. X' f% \/ }+ b+ `( N
  38.     }
    5 V4 `* n' C! \  q1 y
  39.    
    % a% G* a: c5 Y" s6 J4 [
  40.     void showC() {
      v5 p5 F  F! _. C. i! ~
  41.         showB();& ^# a2 y0 U' O, W1 R' k
  42.         cout << "c = " << c << endl;
    $ ?, w) A! W5 ~5 C4 h# f
  43.     }
    : C7 z( ]5 A  f) r! q# Q6 h0 j, v
  44. };
    5 \/ w2 ?3 v; B
  45. + d( T6 w9 q- O. N% ~
  46. int main() {# W2 q+ e7 M) ?: h' D
  47.     C c;
    2 M! r6 J0 B! X2 F2 j5 W- T! Q
  48.     c.setA(53); // 调用基类setA()函数+ ~$ s) N& ?( @1 l, S
  49.     c.showA(); // 调用基类showA()函数
    : G  U3 _8 n% Y4 |) E) B2 x% Z
  50.     ) Z: E/ Q! ?3 ^" k6 m3 H
  51.     c.setC(55, 58); // 调用派生类C的setC()函数
    $ o4 i% h, W% J! o0 `
  52.     c.showC(); // 调用派生类C的showC()函数- n! c* j7 m9 B; U7 M" K
  53.     ' {- B& i  Q6 O0 W* p& D; _9 R
  54.     // 派生类C私有继承自基类B, 所以基类B中私有成员b, 在派生类C中不可访问, 基类B中公有成员setB(), showB()在派生类C中变私有. 在main()函数中不可访问
    $ e* A. M5 ~, G& X
  55.     // c.setB(60); // error: 'setB' is a private member of 'B'& ?4 p9 e& b* g+ d8 U) o7 U
  56.     // c.showB(); // 'showB' is a private member of 'B'1 n! y8 O/ S, J; D  _4 ~# K
  57.     /**; x+ E$ g# Q& d6 T
  58.      a = 53
      C, j0 n- a$ L- G- m3 }# s
  59.      b = 58
    8 b0 ^& Y! d+ f
  60.      c = 55. j9 j) [* ?; v  f) ^- z
  61.      */
    7 o3 ^) r5 k5 [8 C( R/ r, G
  62. }
复制代码

/ _" \; 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
代码格式:
  1. 0 r4 r$ O7 v/ k0 {
  2. 类名::标识符
复制代码
# G( r+ W; U/ B2 c8 q& Z; B, `2 `, ]
:: 为作用域分辨符,"类名"可以是任一基类或派生类名,“标识符”是该类中声明的任一成员名,

2 a- c$ I+ C( |, @/ @
像这样:
  1. 4 e5 g4 L% _6 ?8 s4 h
  2. // 基类A, 也叫根类5 p  l+ V. i6 I" g* y
  3. class A {
    - Y7 X( S. q8 z
  4.     public:% Q; }% m5 ]) t6 I# g
  5.     void func() {* t. r7 C; K( j( ^) ~
  6.         cout << "A func" << endl;) Z0 e3 B) d5 L1 m3 B5 W0 q
  7.     }. f3 w8 J# X7 K
  8. };' d/ [, e: ^8 F8 ?" c
  9. 4 q# u. e* |' ^$ D3 w# }
  10. // 基类B, 也叫根类% O( X5 V) J) l( p9 P
  11. class B {1 T4 N' G! ?3 k5 s( Y' [
  12.     public:, \3 J7 [0 T4 `  J, J; o; q/ ]% Q% a
  13.     void func() {! s/ r7 F* y' j* P6 b
  14.         cout << "B func" << endl;3 _  g1 b4 d; p! i
  15.     }4 d9 a- R3 m# M7 s( U
  16.    
    ( u  \  O+ h0 V
  17.     void gunc() {/ P3 M& S8 S. Z: g
  18.         cout << "B gunc" << endl;
    % O& J9 G5 N, r& H" c- e0 m3 h- U
  19.     }$ J- L3 f0 U6 W  X' C
  20. };; o2 ?1 |) P+ T% w; P, F$ R  r
  21. 9 F3 W- k4 L. R  `2 x3 q4 E
  22. // 多重继承
    / |% y7 i' D* \1 q6 I) i. U
  23. class C: public A, public B {
    % X- `; X5 g7 z- _: G" d1 b
  24.     public:; [3 g1 o6 X+ f' M9 G5 i8 S9 E
  25.     void gunc() {
    ) |# A7 f  S5 [" @+ M/ }0 c
  26.         cout << "C gunc" << endl;
    + d/ ^& I8 k; V" k7 N1 v5 O& F* h) r
  27.     }
    4 i+ Q# Q* I( A$ J/ I  p9 R/ k
  28.    
    : ~; n( j# }; ]2 b
  29.     void hunc() {3 W! t1 c9 |; e* |0 N, X0 ]
  30.         /**; n4 r. [' J! j
  31.          这里就具有二义性, 它即可以访问A类中的func(), 也可以访问类B中的func()& b! [) l: j$ m0 V6 E
  32.          */( r$ E/ u' ?1 o. D
  33.         //func(); // error: Member 'func' found in multiple base classes of different types' Z7 V6 S& y# Q9 R. j
  34.     }
    8 I/ t" b% q7 E4 M/ {' q
  35.     5 u2 _' b/ k3 _% s4 s. m
  36.     void hunc1() {
    6 b( s( Z' ]: R# u
  37.         A::func();
    : U9 a6 C$ P7 ^9 b& `  \
  38.     }
    ( Q+ R' v( ]' N% M2 \
  39.     5 x. Y3 E0 |2 v' }6 F
  40.     void hunc2() {
    8 e, x7 s5 C+ n, v! g6 [* v
  41.         B::func();
    ) b3 [; N: b  T' s0 o8 Q
  42.     }
    - A9 o% S/ S' {& \6 w; t
  43. };& G1 S& m6 j% k2 y) U
  44. 5 y6 P1 M; d0 ^0 h2 s! z7 E  I6 b
  45. int main() {
    % D! U' o: S7 a4 O3 H5 B: w3 g9 X
  46.     C c;
    & X5 A1 Z6 E; G2 m' `& B: z& X
  47.     //c.func(); //具有二义性
      ^4 f/ z* c$ {" J' a5 E
  48.     c.A::func();8 p# P0 D7 ?9 C2 I
  49.     c.B::func();
    3 v9 K8 G+ F0 ~$ _5 g9 [1 K, x% f$ Y
  50.     c.B::gunc();1 L! M8 F9 P# s
  51.     c.C::gunc();9 Q% k7 \  S. n! A; ]& i; k) M
  52.     7 G6 Z% c9 t- Y" |8 s7 m
  53.     c.gunc();
    3 s4 t( C, ]8 T* A; ?9 S* N
  54.     c.hunc1();
    & G: {. `6 W, T7 g( L8 o
  55.     c.hunc2();
    % g% s1 m: A/ y
  56.    
    , s# y1 n! E6 N9 Y' ]- E
  57.     /** 输出结果
    + `+ B' r9 f: l4 q( \
  58.      A func0 c7 e  _& a  v
  59.      B func
    ' u5 x/ U; ?8 `0 P. i4 j
  60.      B gunc% L4 H: n+ f/ F/ K+ h
  61.      C gunc) T. j  i6 r  C5 W; ]
  62.      * [! _  o8 L, l4 i0 R
  63.      C gunc // 如果基类中的名字在派生类中再次声明, 则基类中的名字就被隐藏. 如果我们想要访问被隐藏的基类中的成员则使用作用域分辨符B::gunc();
    " a2 m; i6 D/ G& [. g/ \
  64.      A func
    ' H. g+ I2 A- X+ l3 o4 Y1 m1 e2 h; i
  65.      B func. s5 \, q  H- x/ @
  66.      */
    & s# M: I1 g7 Y. g* W- i
  67. }
复制代码
) |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
例如上个例子中
  1. 7 I# D* B+ T& t$ H' z* m
  2. c.gunc() // 输出”C gunc”, 基类B中的gunc成员函数被支配了2 K' \2 S9 C. r
  3. 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
赞 收藏 评论0 发布时间:2020-9-7 14:16

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版