面向对象语言的三特特性:封装、继承、多态。作为面向对象编程语言的代表C++,本文简单总结C++的三大特性。
C++的三大特性为:继承,多态,封装
封装可以隐藏实现细节,使得代码模块化,
继承可以扩展已存在的模块,它们目的都是为了: 代码重用。
而多态是为了实现另一个目的: 接口重用。
(1)继承。
一个对象直接使用另一个对象的属性和方法。
定义:继承就是新类从已有类那里得到已有的特性。 类的派生指的是从已有类产生新类的过程。原有的类成为基类或父类,产生的新类称为派生类或子类,
子类继承基类后,可以创建子类对象来调用基类函数,变量等
优点:
1.减少重复的代码。
2.让类与类之间产生了关系。继承是多态的前提。
3.继承增加了类的耦合性。
缺点:
1.继承在编译时刻就定义了,无法在运行时刻改变父类继承的实现;
2.父类通常至少定义了子类的部分行为,父类的改变都可能影响子类的行为;
3.如果继承下来的子类不适合解决新问题,父类必须重写或替换,那么这种依赖关系就限制了灵活性,最终限制了复用性。
虚继承:
为了解决多重继承中的二义性问题,它维护了一张虚基类表。
(2)多态。
多态是同一个行为具有多个不同表现形式或形态的能力,简单概括为“一个接口,多种实现”
C++中有两种多态: .
1、静多态
----函数重载:包括普通函数的重载和成员函数的重载
----函数模板的使用
2、动多态在C++中是通过虚函数实现的 ,即在基类中存在一些虚接口(基类的函数前加上virtual关键字),其派生类重写这个接口,这样就构成了多态。 这样通过使用基类的指针或者引用指向子类的对象,就可以实现调用子类对应的函数的功能。运行时将会根据对象的实际类型来调用相应的函数,如果对象类型是派生类,就调用派生类的函数,如果对象类型是基类,就调用基类的函数。动多态的函数调用机制是执行期才能进行确定,所以它是动态的。
1,多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
2,多态的前提
多态的存在有三个前提:
、需要有继承关系
、子类重写父类的方法
、父类引用指向子对
3,多态的好处
多态的出现大大的提高程序的扩展性。
4,多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
1)易读性比较不好,调试比较困难
2)模板只能定义在.h文件中,当工程大了之后,编译时间十分的变态
(3)封装。
隐藏对象的属性和实现细节,仅仅对外提供接口和方法。
封装的类有如下的访问类型:
1)公有( public )成员可以在类外访问;
2)私有( private )成员只能被该类的成员函数访问;
3)保护( protected )成员只能被该类的成员函数或派生类的成员函数访问。
封装好处:
1.将变化隔离;
2.便于使用。
3.提高重用性。
4.提高安全性。
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其访问。
缺点:
1)如果封装太多,影响效率; 2)使用者不能知道代码具体实现。
一、 封装
1. 目的
在面向对象编程中,通过封装能够避免很好保护类中的数据,从而保证数据安全。
2. 简述
仅向外面暴露访问、交互接口,而把具体的细节隐藏起来,将数据和方法进行有机结合。
3. 特性
在C++中有三种访问限定符:public(公开类型)、protected(保护类型)、private(私有类型)
访问限定符修饰成员:
public:当类的成员定义为public类型时,能够在类外访问,其中包括该类的派生类。
protected:当一个成员定义为protected类型时,仅能在类型内、友元和派生类访问。
private:当一个成员定义为private类型,仅能在类内和友元访问。(注:派生类不能访问)
class中成员默认访问权限是private;struct中的成员默认访问权限是public。
二、 继承
1. 目的
在面向对象编程中,对于类,通过继承扩展功能,能够实现代码功能重用。派生的目的是实现功能扩充。
2. 简述
在C++中,通过派生机制来实现继承。被继承的类成为父类,继承后的类称为派生类。派生类在父类的基础上增加自己的特性。继承跟派生是相互的。
3. 特性
继承方式有:public、protected、private
继承中的访问权限
三、 多态
1. 目的
多态能够使接口重用,增强程序的可扩充性。
2. 简述
多态可以概括为:一个接口,多种方法。多态是指在相同对象中收到不同消息或者在同个对象中收到相同消息时产生不同的表现。在C++中,多态有两种形式,一种是静态多态,另外一种是动态多态。静态多态是编译时多态,动态多态是运行时多态。
3. 实现
(1) 静态多态:通过重载函数实现,在同一个类中完成
(2) 动态多态:通过重写实现,是通过虚函数完成重写,发生在不同的类(父类与派生类之间)
重载:
重载是指在同个类中,相同函数名,但形参的个数或者类型不相同,在编译阶段,针对同名函数,编译器根据形参不同将这些同名函数生成“不同函数”,在编译阶段就确定,它们的地址在编译阶段绑定,所以称为静态。
重写:
当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的。因此,这样的函数地址是在运行阶段绑定。
多态是由虚函数实现的,而虚函数主要是通过虚函数表(V-Table)来实现的。如果一个类中包含虚函数(virtual修饰的函数),那么这个类就会包含一张虚函数表,虚函数表存储的每一项是一个虚函数的地址(多继承会包含多张虚函数表)。
这个类的每一个对象都会包含一个虚指针(虚指针存在于对象实例地址的最前面,保证虚函数表有最高的性能),这个虚指针指向虚函数表。
注:对象不包含虚函数表,只有虚指针,类才包含虚函数表,派生类会生成一个兼容基类的虚函数表。
虚函数表:
(1) 原始基类的虚函数表
上图是原始基类的对象,可以看到虚指针在地址的最前面,指向基类的虚函数表(假设基类定义了3个虚函数)
(2) 单继承时的虚函数(无重写基类虚函数)
假设现在派生类继承基类,并且重新定义了3个虚函数,派生类会自己产生一个兼容基类虚函数表的属于自己的虚函数表。
Derive Class继承了Base Class中的3个虚函数,准确说是该函数的实体地址被拷贝到Derive Class的虚函数列表中,派生新增的虚函数置于虚函数列表后面,并按声明顺序摆放。
(3) 单继承时的虚函数(重写基类虚函数)
现在派生类重写基类的x函数,可以看到这个派生类构建自己的虚函数表的时候,修改了base::x()这一项,指向了自己的虚函数。
(4) 多重继承时的虚函数
这个派生类多重继承了两个基类base1,base2,因此它有两个虚函数表。
隐藏:
除了重载和重写,在C++中还有一个“隐藏”。隐藏是通过继承,在派生类中创建于父类同名函数实现。具体规则如下:
(1) 如果派生类的某个函数与父类的某个同名,但参数不同。此时,无论是否有virtual关键字,父类函数将被隐藏。
(2) 如果派生类的函数与父类的函数同名,并且参数也相同,但父类该函数没有virtual关键字。此时父类的该函数在派生类中将被隐藏。
纯虚函数:
4. 特性
(1) 因为静态成员函数是类函数(所有对象公用同一个),所以静态成员函数不能是虚函数。
(8) 普通函数不能声明为虚函数,只有应用于继承关系类的函数才可以。
(9) 因为静态成员函数是类函数(所有对象公用同一个),所以静态成员函数不能是虚函数。
(4) 构造函数不能是虚函数,因为虚函数是通过虚函数表,而指向虚函数表的指针是在创建对象后才有,即等构造函数执行完才初始化。
(5) 析构函数可以是虚函数,且析构函数尽量是虚函数。
(6) 内联函数不能是虚函数,因为内联函数不能在运行时动态确定位置。