C++的面向对象
类及其继承就像是生物学上的分类,是对共性和个性整理得出的逻辑结构。
当我们看到一个猴子,我们说它是一只猴子;然后当我们看到一只黑色的猴子,我们发现它和之前的猴子不一样,之前的猴子是一只棕色的猴子,所以我们说之前的猴子是一只棕猴,新看到的这只是一只黑猴。再然后我们看到一只大棕猴,我们会我们发现它和之前的两只猴子不一样,所以我们说这是一只大棕猴或者棕大猴。
当我们发现第二只猴子并对第一只猴子作对比的时候,我们对它们做了共性和个性的分析,将颜色作为个性,将其他属性作为共性,建立了三个类,一个类是共性,剩下两个类分别是共性加上各自的个性。
当我们发现第三只猴子时,又对这三个东西做了共性和个性的分析,这时候就难办了,究竟是体型更重要还是颜色更重要呢,我们只能二选一,也许我们会选择让颜色作为上一层分类依据,而把体型作为下一层分类依据,所以我们又建立了三个类,一个是棕猴类,一个是小棕猴类,一个是大棕猴类。(也许我们会选择让体型作为上一层分类依据,而把颜色作为下一层分类依据,所以我们又建立了三个类,一个是小猴类,一个是猴类,一个是大猴类。)
这样建立起来的结构是一个有向树,建立的过程就是树的向下延伸和向上延伸,之后免不了要对树的结构做一番修改,使其更简明,修改时遵循一定的规则。
树中每个非叶节点是一个对其子节点的共性的抽象,叶节点没有子节点,叶节点是什么以及叶节点的数量取决于我们的认识范围,叶节点的数量决定了树的深度,生物学家脑中的生物分类有向树比我们一般人脑中的要广得多、深得多。
所谓有向树,即指定了根节点的树,所谓树,即无环图。
有向树属于有向无环图的一种。除根节点外,有向无环图的节点可以有多个前继节点,而有向树的节点只能有一个前继节点,它们的区别有且仅有这一个点。
以上仅仅只是完成了分类,每个类的行为还没有考虑。
类的行为定义在类的方法里,而方法需要输出,不然没有意义,这个输出是什么呢?
我们之所以需要类的方法,是因为我们希望类之间能够产生交互,如果一个类在干什么不影响其他类,那么其他类就不需要接收这个类的信息了,如果这个类在干什么不受其他类的影响,那么这个类就不需要接收其他类的信息了,所以是否产生交互取决于类间是否传递了信息以及这个信息需不需要去考虑。
所以这个输出就是类间传递的信息,这个输出存储在类变量里,而方法就是交互。当然输入也要存储在类变量里。
之前我们对类的分类考虑的是类的静态性质,并没有考虑类的行为。这就导致了行为不一定满足继承关系。
“
C++语言规定子类如果继承父类的话必须继承父类的全部属性和方法。
关于你说的鸵鸟和鸟关系的问题,是一直以来C++继承规则中的"is a"和"has a"的区别。
面向对象语言的继承规则规定:如果在逻辑上A是B的“一部分”(a part of),即B的某些属性和方法对A没有意义,则不允许B从A派生,而是要用A和其他东西组合成B。
也就是说,如果鸵鸟只有鸟的一部分功能的话,面向对象的解决方法是不能从鸟类继承,而是用鸟和其他动物组合出鸵鸟,组合过程中,鸵鸟只用除了飞的其他方法。
希望对你有所帮助~~
”
所以,我们对类的分类应该考虑且只考虑类的交互,不能为了不需要考虑的信息去分类。
C++继承是有向无环图,节点代表类,边代表继承关系。C++继承用有向无环图,这是怎么回事呢?
车这种东西是由多个物件组成的,如果是一辆车,还可以把所有的物件放到一个类里,但是如果是很多辆车,车有很多物件是共用的,又有很多物件不是共用的。
可以做一些抽象,抽象出is-a关系来构成类间继承关系,再把剩下的is-a关系构成另一个类间继承关系,这就是多继承。
继承就是单次抽象。
多继承就是多次抽象,一次抽象后,把剩下的再抽象一次。
从单继承到多继承的变化就是有向树到有向无环图的变化。
到这里讲到了面向对象三大特性的其中一种:继承。面向对象还有一种特性,叫封装。
关键字public, protected, private.
Public函数和变量对所有其他类可见,protected函数和变量对类及其派生类可见,private函数和变量对所有其他类不可见。
关键字 friend.
为了提升封装的灵活性,可以使用friend关键字指定函数和变量的友元,这时此函数对友元可见。
默认构造函数和析构函数是public的。
public, protected, private继承
Public 继承:子类中封装性不变
Protected继承: public在子类中改为protected,其他不变
Private继承: public在子类中改为private , protected在子类中改为private,其他不变
到这里讲到了面向对象三大特性的其中两种:继承和封装。面向对象还有一种特性,叫多态。
C++接口,多态,虚函数是什么及其关系
虚函数简单的说就是为了让基类指针能够指向派生类中与基类同名的函数而引进的,这种能力叫多态。还有一个不同是每个派生类继承的虚函数都是这个基类的虚函数。子类可以不重写这个虚函数。
纯虚函数就是这个虚函数在基类里只声明不定义,并在声明后加一个“=0”。如果要实例化派生类,这个虚函数在派生类中必须被定义。
一个类的虚函数的地址在编译时被放在这个类的虚表里。类的每一个对象有一个属于它自己的虚表指针,也叫虚指针,如果一个类有虚函数,它的对象被构建时就会在内存首地址放入这个虚表指针。之后程序运行时,基类指针会去其指向的类的虚表里找对应的虚函数的指针。
非虚函数就是正常的函数,其地址不放在虚表里。
包含纯虚函数的类称为抽象类。
如果要使用多态,那么基类的析构函数应该设为虚函数。
基类中虚函数的默认参数会在编译过程就被保存,多态时,编译器会使用基类的默认参数。
这上面讲的都是动态多态,它在运行时确定调用的函数。
还有静态多态,它在编译期确定调用的函数,比如重载和泛型等等。
接口本质上就是对功能的抽象,给定一个函数名,就是给定了一个功能。它的参数,返回值,所属类不同,实现就不同,这就是多态。
Void*指针
非虚指针是有类型的,这样在编译的时候就可以确定它指向的数据类型。
虚指针是无类型的,相当于它只有一个地址的含义,没有类型的含义,少了一个含义,算是非虚指针的抽象,所以任何非虚指针都可以赋值给他,而它不能赋值给非虚指针,因为非虚指针不知道它指向的数据类型,用类型转换加上类型信息才能赋值。它在使用时经常需要配合其他参数来获得它指向的数据类型。把它作为函数参数时,因为任何非虚指针都可以赋值给他,所以是多态的一种实现方式,和模板类似。
Void*指针不叫虚指针,virtual才翻译为虚,void翻译为无类型,void*指针叫无类型指针。
虚指针应该是一个指向函数指针的指针。