一. 过程式和对象式程序设计
🐶 概念:
- 过程式程序设计(
C
):从上往下,逐步求精,即按照顺序一步一步的把问题解决; - 对象式程序设计(
C++
):在类中定义成员变量和成员函数,通过定义一个对象的方式来对其变量和函数进行调用的方式 ;
🐭 两者的区别与联系:
- 在
C
中,要用结构(student
)的话,需要定义一个属于该结构的变量(学号、姓名、性别、成绩); - 在
C++
中,当我们要用到类(我们可以把结构叫成“类”)的话,我们不叫定义结构变量,而是叫定义一个对象,该对象不仅包含变量,还可以包含函数(C的结构只能包含变量);
🐹 总结:
(1)C
— 结构 ⇔ C++
— 类;
(2)C
— 结构变量:int、double等类型 ⇔ C++
— 类对象:成员变量、成员函数(C++类特有);
二. 面向对象式的程序设计
🐝 特性:
- 封装:突破C函数的概念,用类做函数参数的时候,可以使用对象的属性和对象的方法
- 继承:A B代码复用,即可以复用前人写的代码
- 多态:是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。 Person对象买票全价,Student对象买票半价。
因为有了继承和多态的出现,对基于对象的程序设计进行了升华,从而有了面向对象式的程序设计;
🐜 优点:
- 易维护:由于继承的存在,即使改变需求,那么维护也只是在局部模块(子类中增加或修改功能函数),所以维护起来是非常方便和较低成本的。
- 易扩展:如果有一个具有某一种功能的类,就可以扩充这个类,创建一个具有扩充功能的类(子类,继承父类功能的同时可以定义自己的特有功能)。
- 代码重用:功能是被封装在类中的,类是作为一个独立实体而存在的,因此可以很简单的提供类库,使代码得以重复使用。
三. 继承性
🐾 概述:
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。 在现实生活中也是存在继承关系的,例如:父辈的财产由其后代继承。
💐 特点:
单继承,即一个父亲可以有好多孩子,一个孩子只能有一个父亲,也就是说一个父类可以有好多的子类,一个子类只能有一个父类。
🌸 优点:
- 继承的出现减少了代码冗余(继承使用父类的变量和功能函数就好,不必自己重新定义一套),提高了代码的复用性。
- 继承的出现,更有利于功能的扩展(不仅可以继承父类的方法,同时还可以新增属于自己的方法),提升可维护性。
- 多态的前提(后面讲)
🌷 举例
1.父类Parent
class Parent { private: int a = 20; public: void print() { cout<<"Parent 打印 a "<<a<<endl; } };
2.继承父类的子类Child
#include "Parent.h" class Child : public Parent { private: int b = 30; }
3.继承的实现:
void main() { Child child; //定义一个子类的对象 child. print(); //通过<对象.>的方式可以调用父类的函数print() child.b: //通过<对象.>的方式可以调用自己的变量 }
四. 多态性
✨ 概念
面向对象中的多态是根据实际的对象类型决定函数调用语句的具体调用目标,同样的调用语句有多种不同的表现形态(含义不同)即为多态。
一般多态是在继承关系中出现的,我们都知道,子类继承父类的时候,通过子类实例化一个对象,其对象是可以调用父类中的变量和功能函数的。
那么大家思考一下,当子类中定义了一个和父类的同名函数时,通过子类对象调用该同名函数时,调用的是父类的函数还是子类自己的函数呢?
即子类和父类中出现同名函数时,子类对象应该调用父类中的同名函数还是调用子类中的同名函数问题,这就是多态性问题。不管是继承还是多态,其实都是对基于对象的编程设计的一种升华。
⭐️ 实现多态的三个条件:
- 要有继承关系;
- 被调用的函数必须是虚函数,且完成了虚函数的重写;
- 调用函数的对象必须是父类指针或者引用(即用父类指针(父类引用)自己指向子类对象,然后使用父类指针(父类引用)进行调用)
在类的定义中,前面有virtual
关键字的成员函数称为虚函数;virtual
关键字只用在类定义里的函数声明中,写函数体时不用。
virtual
关键字与多态又有什么关系呢,下面看一下virtual
在类中的用法:
🌟 面向对象新需求 — 多态
首先,看下面父类Parent
和子类Child
中都定义了一个相同的打印函数print()
,那么思考一下最后base->print()
; 执行基类or子类谁的打印函数?
1、父类Parent 和继承父类的子类Child
class Parent { private: int a = 20; public: void print() { cout<<"Parent 打印 a "<<a<<endl; } }; class Child : public Parent { private: int b = 30; public: void print() { cout<<"Child 打印 b "<<b<<endl; } }
2、实现函数:
void main() { //定义一个父类的指针 Parent *base = NULL; Parent p1; Child c1; //调用方法1 base = &p1; //父类指针调用父类对象 base->print(); //执行父类打印函数 base->a = 10; //改变父类中a的值 base = &c1; //父类指针调用子类对象(这是可以的,类型兼容原则) base->print(); //思考一下,这里是执行基类or子类的打印函数? }
正常我们希望如果父类指针调用父类变量地址,打印父类中的打印函数;如果父类指针调用子类变量地址,那么打印子类中的打印函数。
但最终会发现父类指针无论引用的是基类中的变量地址,还是子类中的变量地址,调用的都是父类中的打印函数
现在,编译器的做法不是我们期望的,那么,我们该如何实现我们前面希望的那样根据实际的对象类型来判断重写函数的调用,即:
- 如果父类指针指向的是父类对象,则调用父类中的定义的函数
- 如果父类指针指向的是子类对象,则调用子类中定义的重写函数
解决方案:
C++
中的多态支持C++
中通过virtual
关键字对多态进行支持- 使用
virtual
声明的函数被重写后即可展现多态特性
只需在是上面的父类中的重名函数前加上virtual即可,用法如下:
virtual void print() { cout<<"Parent 打印 a "<<a<<endl; }
一般在父类中重名函数前加上virtual后,子类中的重名函数前可加可不加,系统默认其写了,最好还是都加上,比较明确。最终就实现了根据实际的对象类型来调用相应的函数了。
总结,要想实现根据实际的对象类型来调用相应的重复函数,需要在父类中的重复函数前加上virtual ,子类重复函数不做要求,但最好都加上
练习案例:
//父类:军人 class CSoldier { public: virtual int Attack() { return 25; } }; //子类:炊事兵 class CCookingSoldier : public CSoldier { public: virtual int Attack() { return 15; } }; //子类:特种兵 class CSpecialForces : public CSoldier { public: virtual int Attack() { return 30; } }; //坏人 class BadPerson { public: int Power() { return 20; } }; void BattleResult(CSoldier *c, BadPerson *b) { if (c->Attack() > b->Power()) { cout << "战斗结果 :正义必胜" << endl; } else { cout << "战斗结果 :小偷胜利" << endl; } }
void main() { CSoldier sol; CCookingSoldier cc; CSpecialForces spe; BadPerson bp; //军人和坏人战斗 BattleResult(&sol, &bp); //炊事兵和坏人战斗 BattleResult(&cc, &bp); //特种兵和坏人战斗 BattleResult(&spe, &bp); system("pause"); }
下雨天,最惬意的事莫过于躺在床上静静听雨,雨中入眠,连梦里也长出青苔。 |