抽象类的定义:带有纯虚函数的类为抽象类。
抽象类的作用:
抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。
所以抽象类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。
使用抽象类时注意:
抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。
如果派生类中没有给出所有纯虚函数的实现,而只是继承基类的纯虚函数,则这个派生类仍然是一个抽象类。
如果派生类中给出了所有纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。
抽象类是不能定义对象的。
纯虚函数定义: 纯虚函数是一种特殊的虚函数,它的一般格式如下:
class <类名>
{
virtual <类型><函数名>(<参数表>)=0;
…
};
纯虚函数引入原因
为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
在很多情况下,基类本身生成对象是不合情理的。
例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法: virtual ReturnType Function()= 0;
)。
若要使派生类为非抽象类,则编译器要求在派生类中,必须对纯虚函数予以重载以实现多态性。
同时含有纯虚函数的类称为抽象类,它不能生成对象。
相似概念
包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。
虚函数是在基类中被声明为virtual
,并在派生类中重新定义的成员函数,可实现成员函数的动态重载。
指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。
C++支持两种多态性:编译时多态性,运行时多态性。
编译时多态性(静态多态):通过重载函数实现。
运行时多态性(动态多态):通过虚函数实现。
多态性
虚函数
抽象类
因为编译器必须能够读取这个结构的声明以理解这个数据类型的大、行为等方面的所有规则。
有一条规则在任何关系中都很重要,那就是谁可以访问我的私有部分。
编译器通过读取类的声明从而进行类的访问权限控制, 而友元函数有权访问本类的所有成员, 因而它必须在类内部进行声明, 使得编译器可以正确处理他的权限.
静态多态(重载, 模板): 是在编译的时候,就确定调用函数的类型。
动态多态(覆盖, 虚函数实现): 在运行的时候,才确定调用的是哪个函数,动态绑定。运行基类指针指向派生类的对象,并调用派生类的函数。
参考: 理解的虚函数和多态
函数重载:
同一可访问区域内, 存在多个不同参数列表的同名函数, 由编译器根据调用参数决定那个函数应该被调用
函数重载不关心返回值类型, 但是对于函数类型时关心的, 例如类中的两个函数拥有相同参数列表的同名函数, 一个为const类型, 一个为非const类型, 依旧时属于函数重载.
函数模板:
(模板编译)在定义模板函数时对模板本身进行编译
(模板实例化)在调用时对参数进行替换, 对替换参数后的代码进行编译
模板函数会经历两遍编译:
虽然它和函数重载类似都可以根据参数确定将要调用的函数版本, 但是函数模板只会生成将要用到的函数版本, 而函数模板无论是否调用其代码都会生成.
覆盖: 是指派生类中重新定义了基类中的virtual
函数
隐藏:是指派生类的函数屏蔽了与其同名的基类函数,只要函数名相同,基类函数都会被隐藏. 不管参数列表是否相同。
派生类的对象可以当做基类对象使用, 例如赋值或则初始化等
派生类对象的地址可以赋给指向基类的指针。在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
向上类型转换(派生类转基类, 总是安全的)
将派生类指针或引用转换为基类的指针或引用被称为向上类型转换,向上类型转换会自动进行,而且向上类型转换是安全的。
向下类型转换(基类转派生类, 不安全)
将基类指针或引用转换为派生类指针或引用被称为向下类型转换,向下类型转换不会自动进行,因为一个基类对应几个派生类,所以向下类型转换时不知道对应哪个派生类,所以在向下类型转换时必须加动态类型识别技术。
RTTI
技术,用dynamic_cast进行向下类型转换, 只有存在虚函数的类才能使用RTTI
参考:
浅谈C++类型转换的安全性 - freshman94的博客 - CSDN博客
继承的赋值兼容规则update
继承: 继承是Is a 的关系,比如说Student 继承Person,则说明Student is a Person。
继承的优点: 是子类可以重写父类的方法来方便地实现对父类的扩展。
继承的缺点有以下几点:
①:父类的内部细节对子类是可见的。(可以自己调用父类的方法)
②:子类从父类继承的方法在编译时就确定下来了,所以无法在运行期间改变从父类继承的方法的行为。
③:如果对父类的方法做了修改的话(比如增加了一个参数),则子类的方法必须做出相应的修改。所以说子类与父类是一种高耦合,违背了面向对象思想。
组合(嵌入式对象): 组合也就是设计类的时候把要组合的类的对象加入到该类中作为自己的成员变量。
组合的优点:
①:当前对象只能通过所包含的那个对象去调用其方法,所以所包含的对象的内部细节对当前对象时不可见的。(必须通过嵌入式对象调用嵌入式对象的方法)
②:当前对象与包含的对象是一个低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码。
③:当前对象可以在运行时动态的绑定所包含的对象。可以通过set 方法给所包含对象赋值。
组合的缺点:
①:容易产生过多的对象。
②:为了能组合多个对象,必须仔细对接口进行定义。
参考: 继承的优点和缺点
右值的概念: 将亡值, 不具名变量
右值引用
转移语意
精确语意传递(参数列表分别为左值引用和右值引用形成参数重载)
概念: 其本身是一个左值, 但是它绑定了一个右值, 此右值的生命周期将和此右值引用一致.
优点:
移动构造函数:
避免了无畏的对下销毁和构造的开销
当该类对象申请了堆内存, 并在析构函数中进行释放时, 使用拷贝构造函数可能会存产生也野指针, 而使用移动构造可以避免野指针的产生.
移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的初值是一个右值引用。
也就是说,只用一个右值,或者将亡值初始化另一个对象的时候,才会调用移动构造函数。
作为参数的右值将不会再调用析构函数。
move
语句,就是将一个左值变成一个将亡值。
概念: 当我们使用一个即将消亡的对象A初始化对象B时, 使用移动语意可以避免额外的无意义的复制构造操作, 也避免了释放内存, 新分配内存的开销.
实现:
优点
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8