2.2 继承
, C/ z" ?+ \. f% E l& p面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,使得创建和维护一个应用程序变得更容易。也达到了重用代码功能和提高执行效率的效果。
7 R" x: |6 R- ?4 r当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。一个类可以派生自多个类,这意味着,可以从多个基类继承数据和函数。
Z/ m# U/ Y( q& r定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下: - class derived-class: access-specifier base-class3 B, H, z0 @3 B8 |" h
- //访问修饰符 access-specifier是 public/protected/private中的一个1 e+ a% G( N: t
- //base-class是之前定义过的某个类的名称
7 ^+ Q. k& }1 P$ p. n - //若未使用访问修饰符access-specifier,则默认为private
复制代码继承的方式有如下三种: 公有继承(public):当一个类派生继承公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问 保护继承(protected):当一个类派生继承保护基类时,基类的公有和保护成员将成为派生类的保护成员 私有继承(private):当一个类派生继承私有基类时,基类的公有和保护成员将成为派生类的私有成员 6 e8 M: h; z. C. U2 B
- /***** inherit_example.cpp *****/
; E+ S9 B: X* t+ J6 c+ H - #include <iostream>% V- s" F% K3 c9 U
- #include <string>6 r' U9 D! n5 K5 B; E0 o; [$ y/ A
- using namespace std;
+ S* k( j* i2 D! j$ W. i3 ] - /* 动物类,抽象出颜色和体重,这两种动物都具有的属性 */
7 t' y7 x$ T( \6 \ - class Animal { //基类
" r6 R9 g$ x9 m! | - public:
" ~" `4 r- @1 s: w) ]" T/ U - string color; //颜色成员变量- a8 T3 v) v0 a; E2 z, j
- int weight; //体重成员变量3 H( b2 P% z% q+ C& e" C
- };
- i& G6 g9 q* P1 \ - /* 狗类继承了动物基类,并在狗类里添加自己的属性 */
; L$ \* b6 y* X0 o- a9 h - class Dog : public Animal { //派生类,公有继承了基类* g) z; | k) E- A8 C8 @
- public:0 I i% z2 ^$ w& P
- string name;
* ~' ~2 s1 e5 d - int age;6 L e u! W8 h }' G( Z
- void run();
9 k. U6 P$ ?4 F- Z& p- x - };- N* Q; | p* H ~, }- n2 u2 y; y
- / W5 J8 [# A3 }9 ^% E% s0 J3 j' O
- int main() {& J; k3 _* P$ ?5 g. |. X
- Dog dog;
6 a( M( O( h. F9 V1 ~1 w - dog.name = "旺财";# S! I" t4 B$ L2 p& u
- dog.age = 2;0 D' t& R* |% t( Y1 z( o
- dog.color = "黑色";
3 O2 l' o: X& c1 g - dog.weight = 120;) C1 w! A) q8 N$ i
- cout<<"狗的名字叫: "<<dog.name<<endl;
2 T5 }* S4 ]/ X7 F: A - cout<<"狗的年龄是: "<<dog.age<<endl;
6 `# m$ a2 \7 i, [% j - cout<<"狗的毛发颜色是: "<<dog.color<<endl;
. L+ ^& G8 K7 X( s6 ^0 c - cout<<"狗的体重是: "<<dog.weight<<endl;
j! _9 R+ h, b! a4 G - return 0;# A; M8 k1 t0 _, p- `6 V; |
- }
复制代码执行下面的指令开始编译
* U% g, M6 K: {' b( _- g++ inherit_example.cpp -o inherit_example2 O3 y2 W6 Y; T! Y' l7 b1 g
复制代码执行./inherit_example后结果如下 - 狗的名字叫:旺财
0 [6 k: \6 h& X& }, n" I, j- W - 狗的年龄是:2+ L1 Z0 w6 s' P. f* f: L8 k
- 狗的毛发颜色是:黑色1 i9 k; W1 @) h4 C0 y" h
- 狗的体重是:120
复制代码 / E/ k4 h% O( _- L; y
2.3 重载
: q/ C) H7 H+ M; bC++允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。 / m2 I, [) o) W8 }
当调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策 . @% d7 k3 \& j4 h+ R
函数重载:在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同 /***** func_overloading.cpp *****/. h) o" i# o6 H$ s: G" z9 \- a$ y" L
#include <iostream>
5 y! w7 z8 a7 m# h6 y3 j4 J#include <string># B8 {7 Z& }( V- Q6 D& x
using namespace std;
" N2 j& \. Y* [3 n" x! U# }, t5 _* i: Q6 \3 C- t1 x+ ?
class Dog {
2 z( r U6 H u7 A, B- J0 ppublic:4 R b* V- B9 r0 ~6 a
string name;
. O' j/ w5 r) ~: a void getWeight(int weight) { //写了一个方法,以int类型作为参数
' D+ b# x8 J0 A: [! v cout<<name<<"的体重是: "<<weight<<"kG"<<endl;
2 w& a. r- O. x. K$ Z }
; F( L! ^0 b. b$ G) T' D+ S //以相同的函数名 getWeight,不同的参数类型 double weight,构成了函数重载
9 S; y- W0 @8 S/ r void getWeight(double weight) {
, U9 X W( |9 u% t) b7 _ cout<<name<<"的体重是: "<<weight<<"kG"<<endl;
5 `# V! L7 e3 V; Y7 J4 ~1 ]4 ?' ^7 e }* E8 U8 e4 S% J4 y3 l) f. v
};* Q8 e2 W+ W& c0 F2 P0 n
) u: Q7 q+ s5 h5 X) k6 } ^! Lint main() {
- c W3 |5 g3 }9 I5 p Dog dog;6 v( B( a! L6 u
dog.name = "旺财";
5 V) T; O* g7 D/ ]; p7 J dog.getWeight(10); //传进不同的参数,程序会匹配不同的重载函数
" L# w; I( f+ z1 e* \ dog.getWeight(10.5); //传进不同的参数,程序会匹配不同的重载函数
8 F3 X& f7 ^7 `# Y; ^% w return 0;6 x. v2 j5 D1 P* i
}
6 `! V- I; s9 V4 e* W" o/ D2 w0 T; m
* P3 Q: ^* a% c) f) x4 S A% f/ b+ ], [5 X+ o9 r5 H
执行下面的指令开始编译 - g++ func_overloading.cpp -o func_overloading
* n+ x4 ?6 J% I# m5 R' x
复制代码执行./func_overloading后结果如下 - 旺财的体重是:10KG
/ v8 a- @7 k5 v0 q- { - 旺财的体重是:10.5KG
复制代码运算符重载:实质就是函数重载或多态,目的在于让人能够用同名的函数来完成不同的基本操作。重载运算符的格式如下: - <返回类型说明符> operator <运算符符号>(<参数表>)
) f; \) ?# V' M1 k - {
$ ~$ U/ }7 J5 ^/ _ - <函数体>
$ ?8 V: J( ]; n% c/ T% p3 w& O, Q - }
复制代码- /***** operator_example.cpp *****/0 ~/ I% p. `) h# }* L& g+ @
- #include <iostream>
0 J) d! ~) v3 E - #include <string>3 W! q! N' D+ D
- using namespace std;% L, @ C( E3 H6 e* ^
9 {0 Y- N3 A7 U2 V3 D: P w% l- class Dog {
* D: X* |5 T) Q& `# M, ? - public:3 Z; A9 C. `9 @# b
- int weight;
$ K3 w7 g' c/ o3 l$ A B' Y) { - //重载“+”运算符,注意函数必须与类名同名,把Dog对象作为传递
: R: d0 ^' [9 o" R7 n& @, o8 L - //使用this运算符进行访问,然后返回一个dog对象 a0 }# U; e1 W$ \1 r9 e
- Dog operator+(const Dog &d) {
! ~: i6 I! R8 ]3 _1 `2 E6 ~ - Dog dog;
0 O8 I& J% R) L; ]* v+ j4 V - dog.weight = this->weight + d.weight;
" G" u* L" V+ Y+ w2 |! u$ \! b - return dog;
7 I( w* v1 v( X# f - }
: R% K( T% R: D% T - };
* p2 M0 |7 d9 }; } - - a) [: A2 L: @4 g9 G/ m9 ]
- int main() { r t! K( K d6 f6 l
- Dog dog1;* m) Y8 F$ ^: ^
- Dog dog2;7 T3 J. |: o; \+ ^* [
- Dog dog3;
9 a1 d5 f, Q. ~" a$ G1 q- u# e6 Y
' M: A7 s6 ^# h2 R% _- dog1.weight = 10;) ]% i+ V3 V) f9 Y/ {( B$ `8 L8 j+ K
- dog2.weight = 20;, H- w( d W6 q. U* L8 l
- dog3 = dog1 + dog2;
- r7 {4 V0 o$ S - cout<<"第三只狗的体重是: "<<dog3.weight<<endl;
/ A7 ^- O+ ] |& h - return 0;
$ m9 u% \) g% j0 [; f, O - }
复制代码执行下面的指令开始编译
4 g) Y! [% @: h# A& P4 G7 A. h; p- g++ operator_example.cpp -o operator_example
复制代码
* s6 A% Q4 u$ Z( C执行./operator_example后结果如下 - 第三只狗的体重是:30: s4 R1 w& c- K s
复制代码2.4 多态
0 B3 e+ M2 F# Z# j
5 F9 i8 ]6 Y. j; I( N* EC++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。形成多态必须具备三个条件:必须存在继承关系;继承关系必须有同名虚函数;存在基类类型的指针或者引用,通过该指针或引用调用虚函数
{, v$ U' y" k$ e- I; E 虚函数:是在基类中使用关键字virtual声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。虚函数必须实现,如果不实现,编译器将报错虚函数声明:virtual ReturnType FunctionName(Parameter) . m+ m/ j2 F6 ?' L) C
纯虚函数:若在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这时就会用到纯虚函数。纯虚函数一定没有定义,用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用 ' s$ l5 F" M& b/ S
$ y) x) ], N& W( L8 c+ e6 |
纯虚函数声明:virtual void funtion1()=0 - /***** polymorphism_example.cpp *****/, Q2 ~( ^ U9 L( n; ]+ ?& _* T
- #include <iostream>& T" X, p; @. Y% O" X! d
- #include <string>8 L, b# b- X4 _
- using namespace std;* [3 l% ]' V; T
- /* 定义一个动物类 */! w( L) H% F- g9 p9 z
- class Animal {$ u- Q( G) F4 D6 t$ V
- public:
- g/ i+ I/ P2 J - virtual void run() { //虚函数# [! i+ v5 m; [6 s6 b( i
- cout<<"Animal 的 run()方法"<<endl;4 \' D' t6 X- k( s0 ^, h V W9 p
- }" [4 x% V9 a& \( \* k
- };
' o+ C# H' L- y/ Y - /* 定义一个狗类,并继承动物类 */
( B4 y( u' Q. ~* e6 {. z, a- ~ - class Dog : public Animal {
: l% Y/ m+ @. ` ~ - public:2 ?2 c1 K& B( G ]! A
- void run() {( c2 t- n1 G% f- O8 z, L
- cout<<"Dog 的 run()方法"<<endl;
, n, W! k. ^9 M* o5 f1 G( H - }
& O$ k; j+ j6 ?! h1 [* g - };
E7 l% R' N; D: `- |- n8 t - /* 定义一个猫类,并继承动物类 */
7 q2 E6 W* P; O5 N9 S- ? - class Cat : public Animal {
4 W" R8 K6 X' {( X/ K4 _# b - public:
/ F3 P6 q" Y. g4 a8 i: O - void run() {
! f* T3 V2 ~0 o - cout<<"Cat 的 run()方法"<<endl;3 g4 P$ |1 K k6 Q0 P( B% t
- }1 m/ K* _6 o; {7 O0 X' b
- };5 y) f$ X! \* t7 W! Q
- 4 P& o+ m" F) k7 E
- int main() {
# ]6 s+ {3 x6 d/ `2 L* v0 t' N - /* 声明一个 Animal 的指针对象,注:并没有实例化 */ B! B7 P0 Q( ?8 T1 [
- Animal *animal; / ?' k2 ~- {) I
- Dog dog; /* 实例化 dog 对象 */ - [5 P, m* [3 L4 {2 B) U- p' F
- Cat cat; /* 实例化 cat 对象 */
# L' K: I: e3 J" H" L - animal = &dog; /* 存储 dog 对象的地址 */
3 C4 K* E$ `- U8 A9 ^; z* ~4 [ - animal->run(); /* 调用 run()方法 */
# Q4 y1 M1 l& }0 O; \) k7 K" Z. N - animal = &cat; /* 存储 cat 对象的地址 */ r/ w4 Z" T' Q$ P
- animal->run(); /* 调用 run()方法 */5 ~) B8 H: t$ C0 r
- return 0;; m) C& K, Y2 Y$ O5 G
- }
复制代码执行下面的指令开始编译 - g++ polymorphism_example.cpp -o polymorphism_example: h+ N U1 q- B i5 h% F. d# s
复制代码
2 }% ^! L7 \' z: M' v执行./polymorphism_example后结果如下 - Dog的run()方法$ `7 J! x2 t' g) i f" X
- Cat的run()方法
复制代码 9 F8 O" `( [5 J% N+ z
转载自嵌入式攻城狮 / U& G' o! ]) G' j8 g0 p4 p" E" Q
4 T z/ V/ {3 c. ^) B* _
- B* s* r: s/ `$ |& ]6 D: T* S( r) ~/ x: i( ^6 X3 }* z* H2 ~
2 n# B" O9 I" Q& d0 a |