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

C++——继承与派生

[复制链接]
gaosmile 发布时间:2020-9-7 14:16
  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 Y
1 ~' 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 c
4 g2 d  F; o0 v
   代码格式:

  1. 3 q# Z8 d& l3 \
  2. class 派生类名: 访问控制 基类名 { 2 z3 r  x/ \# u6 U% Q: I- K7 m! z
  3.         private: 成员声明列表 6 B( `, {. A: b$ t0 v1 M, G
  4.         protected: 成员声明列表 4 z$ S$ C4 X% U
  5.         public: 成员声明列表
    ( }  u: r5 F. H9 q7 n6 Q
  6. }
复制代码
) b  `$ p2 f* ~' |& Z: F3 |
“冒号”表示新类是哪个基类的派生类;“访问控制”指继承方式。
三个方式:public、protected、private

6 r8 Q& P& E3 G$ E& l  I: l02派生类的构造函数和析构函数( L! G7 K  G+ A: {

  1.   l0 W5 j/ k& N/ N4 M
  2. // 基类
    1 f: D) [9 l3 ~2 E, L: |: k
  3. class Point {
    $ {4 f! Q7 \2 x0 U+ }$ z7 A, _1 b7 E
  4.     int x;/ o9 j& a& [/ T( I! k. k2 {
  5.     int y;' p* w( I6 b9 [/ B! X& @5 J
  6.    
    9 U# s! w, b, ^. H  q  v7 }0 k( h
  7.     public:2 z% K, O" |/ s( Y9 F& _
  8.     Point(int a, int b) {
    ; O* m: E  v; k
  9.         x = a;8 `1 Y" r9 _' W$ L* w
  10.         y = b;) l8 @6 D1 @+ R  V; B" b3 T4 u
  11.         cout << "init Point" << endl;. N* @- \! W7 k9 n2 M$ D
  12.     }
    1 S9 t: X1 u, R' I/ Q; ]$ }+ t
  13.     void showPoint() {
    . T8 l! d: H. |3 x" Y8 q6 p0 r
  14.         cout << "x = " << x << ", y = " << y << endl;0 v3 c) h/ |% I& p2 w7 n% V
  15.     }2 J- I8 R' O+ L% v( l  ], t* [, ?8 b
  16.     ~Point() {
    : K8 O3 x% T. j/ H+ @) {, c1 P
  17.         cout << "delete Point" << endl;
    7 p/ Q9 N# j6 L& A1 E# n3 h
  18.     }& v. m  \+ d; B+ M
  19. };
    7 U7 b0 J  W# N

  20. 3 z0 ^! p0 @& ]( Q8 n- C+ C
  21. // 派生类( V$ t( M& j: y% w4 d4 I2 ~
  22. class Rect: public Point {8 y3 u8 ~' ~; c, P  I/ }8 ^, @8 H
  23.     int w;1 p  J9 \# E: K' P6 w! B
  24.     int h;8 }7 J! d0 ?( o5 R' H
  25.    
      {4 _* s/ }1 y& n  n$ a$ B  k
  26.     public:
    ( p3 s2 Q6 C% y
  27.     // 调用基类的构造函数对基类成员进行初始化5 R4 \% ~5 x" I9 ^, G4 n
  28.     Rect(int a, int b, int c, int d):Point(a, b) {
    ! @" y$ b4 _) s2 }
  29.         w = c;+ a9 {" U& L8 z6 W) Y
  30.         h = d;, a1 f' r3 a" g! o: q; j& W5 Z
  31.         cout << "init Rect" << endl;
    1 ^6 b; \% x) Z% e; w
  32.     }( U7 r2 C  a7 ^1 Y) a
  33.     void showRect() {6 O, N: e! P' x( W! {) a
  34.         cout << "w = " << w << ", h = " << h << endl;
    ; N3 e1 u# X$ {; h
  35.     }
    ; N2 X) H, r7 I1 j( z/ d9 g
  36.     ~Rect() {
    * P3 Z' U; @+ d/ j
  37.         cout << "delete Rect" << endl;
    " O: P/ C1 {% u. I, c/ c
  38.     }
    9 n  R. G  p2 @2 H! d" r. f
  39. };
    1 i) J. U3 z! l0 l/ r

  40. , u3 w* |/ |) J2 B
  41. int main() {5 x! w8 _9 E. q7 U
  42.     Rect r(3, 4, 5, 6);
    - i( y2 d. I4 Q1 c  S2 H
  43.     r.showPoint();
    3 j7 r/ N, i( t7 p2 n/ X1 ^) z
  44.     r.showRect();
    # c. [. H4 e- U5 o+ U* J: i$ J
  45.     ! n7 \9 X+ {8 F
  46.     /** 输出结果
    / @4 l$ [% }1 u- G$ |
  47.      init Point // 当定义一个派生类的对象时, 首先调用基类的构造函数, 完成对基类成员的初始化
    2 v1 ~% g' a0 s: D
  48.      init Rect // 然后执行派生类的构造函数, 完成对派生类成员的初始化# m  U! U% F/ Q- D" D2 Q
  49.      x = 3, y = 4 // 调用基类成员函数showPoint();8 p) \& U6 q2 f9 Q8 Z* k! n9 B- R
  50.      w = 5, h = 6 // 调用派生类成员函数showRect();7 r, B& ~& n5 c. t
  51.      delete Rect // 构造函数的执行顺序和构造函数的执行顺序相反, 首先调用派生类的析构函数: {' F" w- R9 y  r. ~0 c
  52.      delete Point // 其次调用基类的析构函数' g: x0 y5 }1 |* ~% ~
  53.      */
    9 n+ T9 \1 ~7 o) f  b
  54. }
复制代码
5 ~6 |2 o, d" {# e
03类的保护成员
! p+ `* G+ b3 X( f  v
9 }) @& K1 B% @- S! y, e& s) z+ F- S' h
如果希望Rect中的showRect()函数可以一次显示x、y、w、h。我们直接修改showRect()函数是不行的。

  1. : v6 I, f0 M# S) s( o
  2. void showRect() {
    , o( p5 l; W# r+ X8 j0 w1 h5 m
  3.   cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;4 E# V9 G! M% `; z& C" H! x( A
  4. }
复制代码

+ 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  [
像这样:

  1. 9 E6 E4 A& ^0 e" P0 I
  2. // 基类
    7 p& T1 |; D* y; Q
  3. class Point {3 M9 ]& e: P3 I/ R' N6 q, a
  4.     // 公有数据成员3 X$ @  K5 m. h9 U4 F  |
  5.     protected:+ e! a9 n7 [- V* a& d  v; ]
  6.     int x;
    7 J; r# N; f! D0 D: m& Q0 i5 t
  7.     int y;
    $ V& l5 w; y- t! ^  h
  8.     0 r# {# S: [% U
  9.     public:- w2 B, n1 ]5 X; m+ L: u
  10.     Point(int a, int b) {% _$ h  N+ B( z+ L% y/ T; A
  11.         x = a;
    ) u; i, \! S8 W$ ^* n: t
  12.         y = b;" g' M$ g, n8 G6 D7 W' t$ p( I
  13.         cout << "init Point" << endl;4 D% D9 O9 N- G: i9 u, d
  14.     }
    1 r. ~, Y% Z& u
  15.     void showPoint() {
    # |8 X* C1 u. W9 A
  16.         cout << "x = " << x << ", y = " << y << endl;) ]5 b5 |7 @1 q/ y8 U- L! }5 \, C
  17.     }: i7 e& {) P! j& e1 s/ j- d
  18. };
    - ~* K2 _2 g2 u8 B" w
  19. / Z# Z# O6 s* s/ ~
  20. // 派生类
    3 G+ k  S3 h$ \) L% _  C" x
  21. class Rect: public Point {7 q$ }/ @% M6 M; L" @! A
  22.     int w;) i$ d: u2 J# I/ U9 V
  23.     int h;
    8 Q/ H8 _. b8 ?: Y) r  e) c
  24.    
    8 p" B' z6 {8 q! X0 s/ G
  25.     public:
    # j) h5 e' M8 P+ B
  26.     // 调用基类的构造函数对基类成员进行初始化. ?7 I2 }/ [; z& i! C
  27.     Rect(int a, int b, int c, int d):Point(a, b) {! p) r( H. K3 P# z3 ?
  28.         w = c;6 B# v5 z3 D$ P. V; C
  29.         h = d;
    " s& G6 Z" Q, P' P! b; P
  30.         cout << "init Rect" << endl;  v' m# Q  [$ O
  31.     }
    9 P" S4 |6 t' x) \$ ]9 H
  32.     4 P" J, z8 y0 y( l
  33.     /** 公有派生, Point类中的受保护数据成员, 在Rect类中也是受保护的, 所以可以访问  // 而通过公有继承的基类私有的成员, 在派生类中是不可被访问的    void showRect() {3 f0 t  v" D  x; {) f3 k
  34.         cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;
    + O6 ]3 }0 e+ k, z2 A* Y7 d
  35.     }*/
    / b/ G0 t4 h* `! r) `+ H
  36. };
    ( F8 |. [% _5 b6 ^
  37. - o1 _* M$ L% i' ]: m. O+ X7 N
  38. int main() {2 r% s2 ?9 k( ]1 S! [% X
  39.     Rect r(3, 4, 5, 6);
    ) ^0 Z( a! r' U, Y
  40.     r.showPoint();- t% A% w) q: {! L
  41.     r.showRect();
    8 O9 D4 t  d: [. ]0 Q  x, X4 u
  42. }
复制代码
' 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
像这样:

  1. $ A9 I& g3 t7 B* L7 ~3 Q
  2. // 基类
      ]$ T6 i& ?! `8 a; ]' y
  3. class Point {
    + E  z5 P+ y: I6 z/ e8 @; y/ o
  4.     // 这里声明成员属性为受保护的1 r. z3 \6 {+ f4 h9 g7 n6 E
  5.     protected:
    , _  K+ S: q* K3 ?
  6.     int x;
    ) O( g! y' \* Z! `
  7.     int y;8 ]0 t- F- F, Q1 D  I3 A
  8.    
    5 k8 v' r! W1 \+ v: v, a5 o
  9.     public:
      S1 w8 e5 p/ k' F4 G
  10.     Point(int a, int b) {
    / y9 U7 w- ~: _5 q) O2 _0 ^+ q
  11.         x = a;
    ; r. B) G$ e) k3 l& Y7 c
  12.         y = b;, e! {3 U9 S  I0 p" \
  13.     }
    & ~& P5 N5 O* N8 M  N
  14.    
    ! U/ d) G1 v* R2 c' y* y: ^
  15.     void show() {1 j4 B- s8 D3 w. M& T: ?
  16.         cout << "x = " << x << ", y = " << y << endl;
    ) |% ~. v) g4 X, Q: {( E( t4 C
  17.     }, ~8 I4 M7 A( [0 L
  18. };
    * d& z1 p/ ~6 b) n2 @( x! F
  19. " b3 _# ]( b/ V/ y1 `4 a0 Z+ H
  20. // 派生类
    . \3 e/ X0 r! K' {/ X( J" l& p
  21. class Rect: public Point {* V, Q- }: {+ E) s( `
  22.     int w;
    1 [5 Z% I( H; o( U: c0 S: t$ s2 i
  23.     int h;
    . k9 M9 _9 w; q; P
  24.     6 p9 l9 @: p0 R+ k' s4 ]* {
  25.     public:. d4 N2 X3 n+ V9 c# Q6 Y( Q
  26.     // 调用基类的构造函数对基类成员进行初始化" O" h- `! {, o5 M: l" p
  27.     Rect(int a, int b, int c, int d):Point(a, b) {
    . G6 }1 f7 p4 l- l7 K
  28.         w = c;: a, J' r4 J) j# \& D2 i3 v
  29.         h = d;
    9 V% b7 a( R% l* e* M6 a6 V
  30.     }
    ' D  T. ~7 C9 m. R7 t
  31.    
    1 p$ K) t3 ]3 [
  32.     void show() {
    * V' A( U, k9 `4 }* t4 M
  33.         cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;" f, e# M# P: u) Z! Y3 o
  34.     }3 m2 o5 v" K& U
  35. };
    ; G. K" P, e. R; g
  36. 2 l/ o: y2 O" i- W9 `+ c7 o
  37. int main() {  W% z4 P4 s$ R/ m2 @" @; ^$ A
  38.     Point a(1, 2);- h9 ~5 }6 x- i& r$ M
  39.     Rect b(3, 4, 5, 6);# U' |' C* J. @$ j) u/ s+ }7 d( d
  40.     a.show();8 q* |2 F1 X" a% w
  41.     b.show();
    9 w* G6 {3 k" C  _( E. ?" _& R9 g
  42.    
    ; e; v$ H- O7 i1 h5 {# P
  43.     Point & pa = b; // 派生类对象初始化基类的引用
    0 s. {- L, C  Z3 V9 \9 K# E3 i
  44.     pa.show(); // 实际调用基类的show()函数) s' \8 k) x: j/ [; o. c
  45.    
    6 p# M/ o5 j% V7 Z2 U$ w
  46.     Point * p = &b; // 派生类对象的地址赋值给指向基类的指针& `) S8 K  n1 F3 {4 s0 Q
  47.     p -> show(); // 实际也是调用基类的show()函数
    7 C/ U1 h# W! c7 ]2 n8 [$ R
  48.     4 F1 y6 M' t3 X/ ?8 R9 \) L  z  |2 }
  49.     Rect * pb = &b; // 派生类指针
    $ m9 l2 J4 }5 W4 p; ^7 V; i. }
  50.     pb -> show(); // 调用派生类的show()函数
    3 n3 r( L' O' s& m2 r) @. \! }
  51.     ) V5 k4 {# O2 U2 b3 a& u# h0 _& P
  52.     a = b; // 派生类对象的属性值, 更新基类对象的属性值! p- N' g0 i  ~- N6 R. @: k
  53.     a.show(); // 调用基类的show()函数6 q8 g( s& x6 b- ?) M  H
  54.     /**. n/ C, o5 R, e  v/ y3 N
  55.      x = 1, y = 2, d* O) y. @8 Q4 }. l0 i8 f2 ^
  56.      x = 3, y = 4, w = 5, h = 6+ g% [$ ^0 k: d
  57.      x = 3, y = 4
    * ?/ I& p  {$ \4 |4 u/ V
  58.      x = 3, y = 4
    8 T) p" h) E9 c# ?" k+ ]8 R
  59.      x = 3, y = 4, w = 5, h = 6; b# h7 A* C% i; ?9 i5 a* D% z/ V
  60.      x = 3, y = 4) ~1 C; c7 ^5 v7 t$ l0 R
  61.      */3 `  r$ N" U8 |8 j; W' U
  62. }
复制代码

) 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
像这样:

  1. 3 o% G  z: h# T" H7 O; J
  2. // 地址类
    1 M" Z  i% d% A
  3. class Address {};6 S' S& ]) s& G  @/ ~' k
  4. class PhoneNumber {};: r7 i, s! h2 |' }

  5. ) }7 U6 t8 Y9 h9 u' `! I  r
  6. // 职工类
    * g9 b  w* p; x) i' @% s
  7. class Worker {; j& k4 S: D# r8 L
  8.     String name;( R# u) V3 Q9 E, t; b! n1 J
  9.     Address address;
    7 W- o% g4 P( A& e. H9 X; a2 B
  10.     PhoneNumber voiceNumber;
      M* s& o2 T! L3 @3 P0 p" N( K
  11. };
复制代码
4 D4 z4 c1 ?: d( B% a
表示一个Worker对象有一个名字,一个地址,一个电话号码,has-a的关系,包含的关系。

5 E- X! t4 C7 _2 ?
(3)私有派生

4 w' ~3 r$ R  u5 Q
通过私有派生,基类的私有和不可访问成员在派生类中是不可访问的,而公有和保护成员这里就成了派生类的私有成员。
  1. " f6 `: F, H  m# E2 f  X  J) ^$ t
  2. // 基类
    # O4 S: _* r7 @" g/ s- B/ [
  3. class Point {% A" D& H2 `0 L' V- H7 }  [
  4.     int x;/ |; r; C5 O) n! z
  5.     int y;
      J9 c/ @. `1 H
  6.     4 G# y8 _3 k+ d: Q' o. E5 R
  7.     public:8 Q  o5 x9 w7 ^1 v
  8.     Point(int a, int b) {
    ; {  U5 d: v( K) m, e7 F
  9.         x = a;9 T# c; B9 J0 e# f
  10.         y = b;6 }, g0 O1 _% ?
  11.     }
    9 k4 n1 X; N/ s% Y
  12. 9 E* e- P5 t) O: V  Z
  13.     void show() {
    ( L0 N6 w/ z! n' M/ Z6 p
  14.         cout << "x = " << x << ", y = " << y << endl;' z% W' ]/ Q" C+ ]# a$ ~9 V
  15.     }% P# I7 T" T- t3 N) o) X
  16. };
      H  ?( H& S, c8 q& D

  17. 4 ~) i, R9 h$ u2 k' F) l/ d
  18. // 派生类
    6 n4 s" @" v% b5 K
  19. class Rect: private Point {* D5 _; r, H6 p
  20.     int w;
    8 `/ `; z# k7 Y! r2 b0 }
  21.     int h;
    0 r+ N  ~8 c1 k- L9 p- V$ H
  22.     5 {# _. A( o8 Q
  23.     public:( h5 S, _2 C- Q# k$ z
  24.     Rect(int a, int b, int c, int d):Point(a, b) {
    3 J+ P5 y. |( j* G  U2 Z
  25.         w = c;6 ?0 l$ c2 z. q$ T5 B* P
  26.         h = d;  e6 B7 Q" ^: \& R5 ^/ ]
  27.     }/ {0 O3 O3 D* D$ \  G
  28.     & H8 Y3 A* j, h$ {4 l6 C
  29.     void show() {
    0 f& R+ w% n5 b3 ]2 r) P
  30.         Point::show(); // 通过私有继承, Point类中的公有成员show(), 在Rect中为私有1 |6 v/ @' {% D3 N
  31.         cout << "w = " << w << ", h = " << h << endl;; I+ D/ j: g. c8 l9 N
  32.     }/ N4 S4 W& G0 F) J( A, W
  33. };
    . e. q; f" h4 u# I

  34. / M! g4 {2 z3 \& T, b! y
  35. class Test: public Rect {
    8 y) \3 E2 v3 }. @. q% d
  36.    
    ! L  n, Q: t9 B& ?
  37.     public:
    7 y1 {8 k+ Q- n& b
  38.     Test(int a, int b, int c, int d):Rect(a, b, c, d) {9 y6 o* K9 Y5 @% S
  39.         
      a9 ^$ Y  R  j2 z& i6 C) d
  40.     }- L1 f0 ?9 J. n- ]4 p7 s( r7 z1 t
  41.     void show() {
    & {7 ^9 i& {. w  d6 a- n
  42.         Rect::show();
    0 J, Z* x: _( g7 c4 y, B0 p' S9 G" v
  43.         //Point::show();! W* u8 i0 M* J3 y5 y9 x% B
  44.         /** error: 'Point' is a private member of ‘Point’1 W+ ^: V( g8 s8 B/ Y" W
  45.          标明: 不可访问基类Point中的成员" e: o' A1 [4 N8 @1 a1 J
  46.          Rect类私有继承自Point类, 所以Point中的私有成员x, 私有成员y, 在Rect类中为不可访问: Point类中公有成员show(), 在Rect中变私有, ~6 J- K$ q' Q
  47.          Test类公有继承自Rect类, 所以在Rect中成员x, 成员y, 仍然是不可访问, Rect::show()还是public, 但是Point::show()不可访问 */
    " ^3 Q' a. M% t/ M  b. `8 b2 S. g
  48.     }
    & ~# ]2 }8 v1 X
  49. };
复制代码
! 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. 1 m: y' f0 y2 ]2 |! h
  2. class 派生类名: 访问控制 基类名1, 访问控制 基类名2, … {
    / M& c$ s( [& Z9 i0 j- w6 }- ~
  3.     //定义派生类自己的成员
    ' N7 C' u8 Z4 B- \: o+ u
  4. }
复制代码

. _$ D9 J+ W5 R0 P' P& y# n8 ^
像这样:

  1. 8 E* J- p1 y8 g& L3 x2 ?5 |: S% D
  2. // 基类A, 也叫根类$ m+ L' a1 I3 a0 h/ \( I2 b- n2 X1 s
  3. class A {0 q# |) {* d1 _" ?4 k: R
  4.     int a;$ p: x3 h& j, c7 T: c: r
  5.    
    % ?; ?# b7 w5 Q
  6.     public:9 q* X  b/ i2 U4 V. F4 a6 w3 ~" S
  7.     void setA(int x) {
    ( e9 |8 o4 M8 Z# \8 W8 B4 z8 p8 X- v
  8.         a = x;
    % [* f5 S5 R& G, _
  9.     }
    % f/ w2 a$ i* z  ~) K
  10.     9 E% D0 g: }# `. P1 [$ H9 C
  11.     void showA() {
    & i1 p1 |2 v0 W/ B
  12.         cout << "a = " << a << endl;
    ; [& _6 b3 m& c& L1 x
  13.     }% V/ @( V9 P* S4 c" P
  14. };! l6 H" g; p% U# ~) f

  15. ( \# i4 T6 Y' O
  16. // 基类B, 也叫根类
    + X' K7 X; C9 C. T  h* A
  17. class B {
    ' I$ Y& W8 C/ V7 i* g% t
  18.     int b;9 k) O* Y$ _9 T
  19.    
    2 P0 z  c" Y) X& ^: m
  20.     public:' r+ }) Z( s6 ^+ |" K
  21.     void setB(int x) {0 Y* M0 o- x0 k. P  e
  22.         b = x;
    ' W5 L# p5 E8 i6 l) m
  23.     }) x5 {: f/ }/ j
  24.     ' X; w/ p* r& w
  25.     void showB() {* r' g" t4 s) |9 b3 h0 o% S
  26.         cout << "b = " << b << endl;
    $ V3 d# H) Z7 Y" D5 Q( B
  27.     }- x3 Q9 N7 }! D3 s* O
  28. };
    " ~4 Y0 a0 p1 [# F) X+ R2 ^/ \

  29. * Z7 S- L) s) d" f4 u" q, H; b
  30. // 多重继承, 公有继承自类A, 私有继承自类B3 s/ @/ f, }+ j- I
  31. class C: public A, private B {: x3 c8 Y& P) c
  32.     int c;/ c8 `0 }% V/ o
  33.    
      k5 I# [7 ]" I
  34.     public:" b# t+ F1 y4 s! {) I' G! Z
  35.     void setC(int x, int y) {
    2 R' R7 b- `% b5 @, u' p
  36.         c = x;
    % D6 j- E+ t% @% |. w& M
  37.         setB(y);
    & L% f& p5 o5 T0 s1 b/ V
  38.     }4 {. P0 D, H/ ^' m9 q$ E
  39.     # u4 M! t+ [8 p7 O$ {  Q+ D5 A
  40.     void showC() {8 E- g5 m( t" G0 y7 L" t  M3 f' G
  41.         showB();
    . G) M! G: k+ d1 ^$ q* v% z  g" V
  42.         cout << "c = " << c << endl;
    ! j5 e% h  I+ m4 @9 u
  43.     }% }6 [4 U) ~$ J. e0 {
  44. };1 ^  U1 }8 a5 J4 z; s

  45. : U, K' X7 T; P& B5 X6 H- @
  46. int main() {! m. E; I8 i  |: Q6 Q
  47.     C c;
    1 z% \4 ?5 e  _  e1 `5 n3 Q
  48.     c.setA(53); // 调用基类setA()函数
    6 H  N; u) |* K' E% m0 z( x, {
  49.     c.showA(); // 调用基类showA()函数& s1 k* a. Z3 v* l& j1 [0 {/ W
  50.     7 [  n/ G$ @; ]" N
  51.     c.setC(55, 58); // 调用派生类C的setC()函数
    0 Y, E$ h  i# _
  52.     c.showC(); // 调用派生类C的showC()函数
    + L' d' U' B" ?: Y" F' O/ e
  53.     * `$ V2 h+ U7 R9 E" Y
  54.     // 派生类C私有继承自基类B, 所以基类B中私有成员b, 在派生类C中不可访问, 基类B中公有成员setB(), showB()在派生类C中变私有. 在main()函数中不可访问
      j/ o  e! b% p# ^) j, L  E, {
  55.     // c.setB(60); // error: 'setB' is a private member of 'B'
    $ G% `3 o/ }' k% |; G0 _% z
  56.     // c.showB(); // 'showB' is a private member of 'B'  ?9 h& H: @1 l* u3 f; \: L5 V5 a
  57.     /**+ A1 h) E" {& e' c, k6 F- z
  58.      a = 53
    & y) n; c7 ^" B+ E
  59.      b = 582 A1 K8 Q6 x2 ^& T# `- z% T! F2 q
  60.      c = 55/ y! G6 m, x, ~% i' S
  61.      */1 A9 U% g, Q- p8 g2 T
  62. }
复制代码

: d! r1 H) o% R0 K, u二义性及其支配规则
/ j7 G; ?) c6 o; ]; J/ a$ C9 e
5 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
代码格式:

  1. ! g! r8 r; ?% H  f2 M# I0 s! O( C
  2. 类名::标识符
复制代码

: E4 a# l# f9 A  _
:: 为作用域分辨符,"类名"可以是任一基类或派生类名,“标识符”是该类中声明的任一成员名,
7 d# K0 e* B; `2 }. P5 ~" @  u5 j. B
像这样:

  1. 6 Y" Y* K+ n& ]3 K2 O% H: f4 W3 ~0 G
  2. // 基类A, 也叫根类
    0 l6 e& I5 X/ D. O/ v
  3. class A {
    , x$ J* s# T2 ^, S* x% v
  4.     public:
    " y6 K- F( d$ @" J( X
  5.     void func() {7 j- \$ C& C. k+ u# H3 q" Z
  6.         cout << "A func" << endl;
    + x/ y* }3 A: u  a; L
  7.     }: G! r. Z4 G( v' n4 [; j0 H  Q& v3 C* o
  8. };% J" U1 O; `% d( u: K+ O! M
  9. / p2 r  U! `3 Z' J; f
  10. // 基类B, 也叫根类
    9 S/ E6 ~' V- J7 i6 j/ C, E! {; V
  11. class B {$ o: \" b2 a- L. c& Y7 p  v
  12.     public:$ `" k4 Q1 }- C" L4 H
  13.     void func() {
    + E" m$ |, V. ^" l2 B2 ~. U
  14.         cout << "B func" << endl;
    3 J: l7 J7 f# @# ?9 ]( M
  15.     }) }0 Y6 B  @9 n' q
  16.    
    " ?/ E, C6 W1 j( t
  17.     void gunc() {
    5 e) m) h) e( D& m- S! t+ t
  18.         cout << "B gunc" << endl;; h: k+ o( c% B5 p5 J3 L
  19.     }
    * L& E: G; S5 H9 v9 p  b
  20. };
    % V6 j! F( o. }: r- W7 W

  21. + s" j/ N2 Y& m5 M  O# s8 C
  22. // 多重继承
    ; n0 N+ ]8 g* _
  23. class C: public A, public B {
    7 l# t% J: t# C3 h1 B
  24.     public:# y! k0 t1 O3 a/ o- k
  25.     void gunc() {6 T1 ~/ M0 q4 @0 x* G
  26.         cout << "C gunc" << endl;
    ! g" J+ n9 o; R+ F- h' U9 _
  27.     }
    ( P# `) ^" _# z: [
  28.    
    ! v' r- b, O4 w; o
  29.     void hunc() {
    ; y, M1 \8 B) A& f
  30.         /**
    4 z0 c( A7 @5 h8 u* d
  31.          这里就具有二义性, 它即可以访问A类中的func(), 也可以访问类B中的func()
    " h9 t( F1 k5 A2 _9 t. w
  32.          */
    / S% ], ]; S. s+ t) e+ x3 v, F
  33.         //func(); // error: Member 'func' found in multiple base classes of different types
    - B& @; q, B* P& R9 j, O; U
  34.     }
    3 u9 Z* x8 t( [/ ^
  35.     & ~8 E* q; T& o8 n+ t
  36.     void hunc1() {
    7 @( H: A, J+ M) C7 b
  37.         A::func();
    4 V& R4 h$ J% U5 c- H1 B0 t
  38.     }
    # }  ?# y, X: [& ^9 ]
  39.     " ?3 v/ E8 L4 e# z, m# @
  40.     void hunc2() {( D6 W3 Z8 d9 ?" H) V4 u
  41.         B::func();
    : o) i0 G/ ~" @) Y1 z
  42.     }% v% j. K. V" `: g8 o
  43. };
    8 \. a: d: o: B  Y; W9 ^% o

  44. * o1 @- Z: E5 O: ]! \1 B* N5 M1 u
  45. int main() {  `: l7 d" k& H+ `' {- A% @
  46.     C c;
    % a+ P) A! C# f7 e3 T& P# q8 P
  47.     //c.func(); //具有二义性
    0 I* }5 O! o5 J# |# q- f7 R5 Q. U
  48.     c.A::func();: o% A2 ~- x2 j* l4 z
  49.     c.B::func();
    ! b' b: ^# o& u; }* _& s  O" Q2 R
  50.     c.B::gunc();, v! }- c; |/ z; T1 k: D3 P. H* P( [
  51.     c.C::gunc();% y' L4 v0 \& z) Z# ~, h
  52.     ) M, X- s, Q; d, y! ]) v) C
  53.     c.gunc();
    . o" z+ g4 r2 T- g5 T
  54.     c.hunc1();) _! x# a+ p4 g6 H
  55.     c.hunc2();
    9 H7 `7 W2 R2 v$ B$ ~. V4 z
  56.     0 n4 w$ I& u4 r+ v* `+ }
  57.     /** 输出结果8 S% R) W4 b8 R7 j
  58.      A func
    $ z" J/ z# c! F, ^8 M. t, [
  59.      B func: o; n3 v" q8 W" H4 M4 s
  60.      B gunc
    5 \6 l' y: N' y+ r7 H: L# d' [# f" h
  61.      C gunc" L6 n0 b- d- |5 w) d3 [
  62.      5 O9 s2 T' r) v3 F. h( H
  63.      C gunc // 如果基类中的名字在派生类中再次声明, 则基类中的名字就被隐藏. 如果我们想要访问被隐藏的基类中的成员则使用作用域分辨符B::gunc();. r% z0 D5 L. F4 m+ a
  64.      A func
    7 p$ r0 _  j2 l+ r
  65.      B func% J, e" R! d, K: L7 R
  66.      */
    , d# q" g" ^# w/ }# {. i
  67. }
复制代码
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
例如上个例子中
  1. # G# h# K$ x% B
  2. c.gunc() // 输出”C gunc”, 基类B中的gunc成员函数被支配了
    ; N' {% f+ Q1 w, u: B
  3. 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
收藏 评论0 发布时间:2020-9-7 14:16

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版