[C++]⾯向对象的三⼤特性:封装、继承和多态
博客主页:
欢迎点赞 收藏 留⾔ 如有错误敬请指白萝卜英语
正!
本⽂由 Loewen⼂原创,⾸发于 CSDN,转载注明出处
现在的付出,都会是⼀种沉淀,只为让你成为更好的⼈
⽂章预览:
⼀. 过程式和对象式程序设计
1、过程式程序设计(⾯向过程):从上往下,逐步求精,即按照顺序⼀步⼀步的把问题解决 ⇒ C语⾔。
2、对象式程序设计:把成员变量和函数包含在类中,需要的时候通过定义⼀个对象的⽅式来对其变量和函数进⾏调⽤的程序,这种程序的书写⽅式,就叫基于对象的程序设计 ⇒ C++。
3、两者的区别与联系:
在C中,要⽤结构(学⽣)的话,需要定义⼀个属于该结构的变量(学号、姓名、性别、成绩)。
在C++中,当我们要⽤到类(我们可以把结构叫成“类”)的话,我们不叫定义结构变量,⽽是叫定义⼀个对象,该对象不仅包含变量,还可以包含函数(C的结构只能包含变量)。
总结:
(1)C(结构)⇒ C++(类)
(2)C(结构变量(int、double等类型))⇒ C++(类对象:相较于C语⾔除了可以定义成员变量(int、double等类型)之外,C++还新增了可以在类中定义函数(也叫⽅法,⽤于实现某种功能,是C++类特有的))
⼆. ⾯向对象式的程序设计
1、⾯向对象的特性
因为有了继承和多态的出现,对基于对象的程序设计进⾏了升华,从⽽有了⾯向对象式的程序设计。它主要有下⾯三⼤特性:封装:突破C函数的概念,⽤类做函数参数的时候,可以使⽤对象的属性和对象的⽅法
继承:A B代码复⽤,即可以复⽤前⼈写的代码
多态:是在不同继承关系的类对象,去调⽤同⼀函数,产⽣了不同的⾏为。⽐如Student继承了Person。 Person对象买票全价,Student对象买票半价。
2、⾯向对象的优点
易维护:由于继承的存在,即使改变需求,那么维护也只是在局部模块(⼦类中增加或修改功能函数),所以维护起来是⾮常⽅便和较低成本的。
易扩展:如果有⼀个具有某⼀种功能的类,就可以扩充这个类,创建⼀个具有扩充功能的类(朝什么意思
⼦类,继承⽗类功能的同时可以定义⾃⼰的特有功能)。
代码重⽤:功能是被封装在类中的,类是作为⼀个独卢俊义人物简介
⽴实体⽽存在的,因此可以很简单的提供类库,使代码得以重复使⽤。
三. ⾯向对象特性—继承性
1、概述
继承就是⼦类继承⽗类的特征和⾏为,使得⼦类对象(实例)具有⽗类的实例域和⽅法,或⼦类从⽗类继承⽅法,使得⼦类具有⽗类相同的⾏为。在现实⽣活中也是存在继承关系的,例如:⽗辈的财产由其后代继承。
2、特点
单继承,即⼀个⽗亲可以有好多孩⼦,⼀个孩⼦只能有⼀个⽗亲,也就是说⼀个⽗类可以有好多的⼦类,⼀个⼦类只能有⼀个⽗类。
3、优点
继承的出现减少了代码冗余(继承使⽤⽗类的变量和功能函数就好,不必⾃⼰重新定义⼀套),提⾼了代码的复⽤性。
继承的出现,更有利于功能的扩展(不仅可以继承⽗类的⽅法,同时还可以新增属于⾃⼰的⽅法),提升可维护性。
多态的前提(后⾯讲)
4、使⽤⽅法
⽗类Parent :
class Parent
{
private:
int a=20;
public:
void print()
{
cout<<"Parent 打印 a "<<a<<endl;
}
};
继承⽗类的⼦类Child :
class Child:public Parent
{
private:
int b=30;
}
继承的实现:
void main()
{
//定义⼀个⼦类的对象
Child c1;
/
/通过对象.的⽅式可以调⽤⽗类的函数print()和⾃⼰的变量
c1.print();
c1.b:
}
四. ⾯向对象特性—多态性
1、什么是多态?
理论:⾯向对象中的多态是根据实际的对象类型决定函数调⽤语句的具体调⽤⽬标,同样的调⽤语句有多种不同的表现形态(含义不同)即为多态。
⼀般多态是在继承关系中出现的,我们都知道,⼦类继承⽗类的时候,通过⼦类实例化⼀个对象,其对象是可以调⽤⽗类中的变量和功能函数的。那么⼤家思考⼀下,当⼦类中定义了⼀个和⽗类的同名函数时,通过⼦类对象调⽤该同名函数时,调⽤的是⽗类的函数还是⼦类⾃⼰的函数呢?即⼦类和⽗类中出现同名函数时,⼦类对象应该调⽤⽗类中的同名函数还是调⽤⼦类中的同名函数问题,这就是多态性问题。不管是继承还是多态,其实都是对基于对象的编程设计的⼀种升华。
2、实现多态的三个条件:
要有继承
被调⽤的函数必须是虚函数,且完成了虚函数的重写
调⽤函数的对象必须是指针或者引⽤(即⽤⽗类指针(⽗类引⽤)⾃⼰指向⼦类对象,然后使⽤⽗类指针(⽗类引⽤)进⾏调⽤)
在类的定义中,前⾯有 virtual 关键字的成员函数称为虚函数;virtual 关键字只⽤在类定义⾥的函数声明中,写函数体时不⽤。virtual 关键字与多态⼜有什么关系呢,下⾯看⼀下virtual 在类中的⽤法
3、⾯向对象新需求—— 多态
⾸先,看下⾯⽗类Parent 和⼦类Child 中都定义了⼀个相同的打印函数print(),那么思考⼀下最后ba->print(); 执⾏基类or⼦类谁的打印函数?
⽗3d电影怎么看
类Parent :
class Parent
{
private:
int a=20;
public:
void print()
{
cout<<"Parent 打印 a "<<a<<endl;
}
};
继承⽗类的⼦类Child :
class Child:public Parent
{
private:
int b=30;
public:
void print()
{
cout&l黄家驹经典歌曲
t;<"Child 打印 b "<<b<<endl;
}
}
实现函数:
void main()
{
//定义⼀个⽗类的指针
Parent*ba=NULL;
Parent p1;
Child c1;
//调⽤⽅法1
ba=&p1;//⽗类指针调⽤⽗类对象
ba->print();//执⾏⽗类打印函数
ba->a=10;//改变⽗类中a的值
ba=&c1;//⽗类指针调⽤⼦类对象(这是可以的,类型兼容原则)
ba->print();//思考⼀下,这⾥是执⾏基类or⼦类的打印函数?
}
正常我们希望如果⽗类指针调⽤⽗类变量地址,打印⽗类中的打印函数;如果⽗类指针调⽤⼦类变量地址,那么打印⼦类中的打印函数。
但最终会发现⽗类指针⽆论引⽤的是基类中的变量地址,还是⼦类中的变量地址,调⽤的都是⽗类中的打印函数
现在,编译器的做法不是我们期望的,那么,我们该如何实现我们前⾯希望的那样根据实际的对象类型来判断重写函数的调⽤,即:如果⽗类指针指向的是⽗类对象,则调⽤⽗类中的定义的函数
如果⽗类指针指向的是⼦类对象,则调⽤⼦类中定义的重写函数
解决⽅案:
C++中的多态⽀持
C++中通过virtual关键字对多态进⾏⽀持
使⽤virtual声明的函数被重写后即可展现多态特性
只需在是上⾯的⽗类中的重名函数前加上virtual即可,⽤法如下:
virtual void print()
{
cout<<"Parent 打印 a "<<a<<endl;
}
⼀般在⽗类中重名函数前加上virtual后,⼦类中的重名函数前可加可不加,系统默认其写了,最好还是都加上,⽐较明确。最终就实现了根据实际的对象类型来调⽤相应的函数了。
总结,要想实现根据实际的对象类型来调⽤相应的重复函数,需要在⽗类中的重复函数前加上virtual ,⼦类重复函数不做要求,但最好都加上
练习案例:
//⽗类:军⼈
class CSoldier
{
public:
virtual int Attack()
virtual int Attack()
{
return25;
}
};
//⼦类:炊事兵
class CCookingSoldier:public CSoldier
{
public:
virtual int Attack()
{
return15;
}
};
//⼦类:特种兵
class CSpe救赎英语
cialForces:public CSoldier
{
public:
virtual int Attack()
{
return30;
}
};
//坏⼈
class BadPerson
{
public:
int Power()
{
return20;
}
};
void BattleResult(CSoldier*c,BadPerson*b) {
if(c->Attack()>b->Power())
{
cout<<"战⽃结果:正义必胜"<<endl;
}
el
{
cout<<"战⽃结果:⼩偷胜利"<<endl;
}
}
void main()
{
CSoldier sol;
CCookingSoldier cc;
CSpecialForces spe;
BadPerson bp;
//军⼈和坏⼈战⽃
BattleResult(&sol,&bp);
//炊事兵和坏⼈战⽃
BattleResult(&cc,&bp);
/
/特种兵和坏⼈战⽃
BattleResult(&spe,&bp);
system("pau");
}