封装、继承、多态(既然是设计思想我想设计模式才是主要的)
封装:public,protect,private
继承:单继承(public,private),多重继承,虚拟继承
多态:静态多态(函数重载,模板),动态多态(虚函数)
封装的目的:隐藏对象的属性和实现细节,对外提供公开接口。降低和用户代码的耦合,提高安全性,方便代码的修改。
例如,如果实现需要修改,只需要修改实现内部的细节,不需要修改用户代码部分。
封装的访问级别:
public:外部可访问,子类可访问,内部可访问
protect:外部不可访问,子类可访问,内部可访问
private:外部不可访问(友元可访问),子类不可访问,内部可访问
继承概念:实现对已有东西的一种共享,某种程度说是一种代码复用,增加类与类之间的耦合
单继承
应用场合:只需要继承单一的父类,比如四边形,矩形,正方形
两种继承方式的意义
public继承描述两个类之间一种is a的关系,能用父类的地方一定能用子类,反之不成立。
private继承意味着根据某物实现出,这样可以直接将一个类的部分功能拿过来用
(直接将一个类作为复合类也可以实现相同的效果,但是当涉及到protect成员(因为复合类不能直接访问)
和虚函数的时候(因为复合类不能重写虚函数,但是作为继承可以重写))
多继承
应用场合:需要继承多个父类,标准输入流类,标准输出流类和标准输入输出流类
虚继承
由于多继承会造成菱形继承,即B,C继承了A,D又继承了BC,这样D中就会同时存在两份A类的子对象数据。为了让D中只保存
一份A的数据,引入了虚继承。
虚继承的实现:引入了一个虚基类的指针,这个指针其实是一个偏移量,指出公共基类在子类对象中的偏移量。
多态
静态多态主要是编译时期决定的,因此叫做静态多态
函数重载——函数名相同,参数不同的一系列函数,在编译时期可以根据参数的不同决定调用哪个函数。
模板——在编译时期根据类模板或函数模板具现出不同的类和模板实现的多态
动态多态主要发生在执行期
动态多态的实现主要是靠虚函数机制
虚函数——通过相同的指针调用函数,但是根据指针所指之物不同,调用的函数不同。虚函数给了子类对象一定的灵活性,
他可以选择继承父类的接口和实现,也可以选择只继承父类的接口,重写实现。而非虚函数只能同时继承接口和实现。
虚函数的实现机制:首先明白编译器为了支持虚函数需要做的事情,然后通过看虚函数被调用的过程明白虚函数的作用机制。虚函数怎么实现的:对于虚函数的支持需要两个东西,虚函数表和虚函数指针。虚函数表用来存放虚函数的地址。虚函数
指针存放虚函数表的地址。虚函数指针存放在对象内部,在对象被初始化的时候由编译器完成初始化。首先复制基类的虚函数表,然后对复制的这个表在修改或者添加,修改被子类复写的函数,替换为响应的子类的函数地址,添加子类新添加的
虚函数的地址。然后再对象初始化的时候将虚函数表的地址放在对象的虚函数指针里。因此子类和父类的虚函数指针是不一样的。可以看到在构造虚函数表的过程中,基类和子类中的相同函数在虚函数表中的偏移位置是相同的,并且偏移位置在
编译时期就可以确定,但是并不能确定的是虚函数指针的值,因为和对象的类型有关,因此要借助运行期类型识别。
虚函数被调用的过程是怎样的:
需要首先明白虚函数指针是放在对象起始位置的,在单继承中,所有继承体系中的类的虚函数指针都在起始位置,因此所很容易取得。在调用虚函数的时候,如果指针所指的对象是基类,那么取得的虚函数指针所指的是基类的虚函数表,因此
调用的将是基类中对应的函数。根据要调用的函数可以找到他在虚函数表中对应的位置,取出对应的函数地址,即可完成调用如果所指的对象时子类,同理。利用引用来调用也是如此。但是如果直接利用对象来调用,并不会触发虚拟机制,而是直
接在编译器就决定了所调用的函数。并且利用指针和引用来调用非虚函数也是在编译时期就决定的。因此要正确的触发虚拟机制必须同时满足两点,利用指针或引用调用虚函数。
不同的继承机制对虚函数机制的影响
单一继承下面,显然运作良好,因为虚函数指针都在相同的位置而且只有一个,可以很容易的取得。但是对于多继承,为了支持多继承,对象的内存布局中是每个基类含一个虚函数指针,子类和第一个基类公用一个虚函数指针,显然对于第一个基
和单一继承下类似。但是对于后续的基类,要找到对应的虚函数指针必须进行指针的调整,调整到对应的基类所在的位置。可以看到多继承还是可以支持多态,只是稍微麻烦了一点。对于虚拟继承,需要根据虚基类指针找到基类然后在找到虚函数
指针再进行调用,显然在虚拟继承下支持多态比较麻烦,因此一般不把公共基类中存放虚函数。
设计模式
创建性设计模式:
工厂模式:将实例的生成交给子类,隐藏创建的细节
例:父类是抽象类,自己不产生对象,调用子类产生子类对象进行返回
结构型设计模式:
适配器模式:对已有接口进行变化使其呈现新的功能(功能相似接口不同的类),可以解决代码复用和兼容性的问题(强调修改接口)
例:stack queue 都是利用deque作为底层容器改变接口之后的适配器容器
实现:可以用继承或者复合的方式改变并使用另一个类的功能(都是根据某物具现出的意思,当然涉及到虚函数和protect的时候只能用继承)
桥接器模式:将类的功能结构层次(父类子类)和实现结构层次分离(接口实现)分离(应该是实现接口和实现的分离)
实现:分别实现功能层次结构,和实现层次结构,然后再功能层次结构中包含一个实现层次结构的指针就可以实现两者的耦合,可以随时更换他的实现层次结构
行为型设计模式:
迭代器模式:提供遍历集合中元素的方法,可以将遍历和数据结构分离
例:stl各种容器的迭代器;
实现:一个集合就需要一个迭代器,一个迭代器类一般都包含一个集合的引用(所要访问的集合),然后要实现++ -- []等顺序或随机访问操作
访问者模式:在访问元素的过程中,对元素进行处理,强调的是处理,将数据结构和处理分离
例:for_each 可以对集合中的元素进行统一处理,还可以通过修改仿函数变换处理的方法
模板模式:在父类中定义了流程的框架,在子类中可以根据需要进行重写
例:(觉得有点像虚函数,好像就是)