-
虚函数定义:
查看全部 -
多态:
静态多态(早绑定):重载
动态多态(晚绑定):以封装继承为基础
查看全部 -
在我们还没有学习多态的时候,父类和子类出现了同名函数,这个时候就称之为函数的隐藏(因为没有虚函数?)
如果我们没有在子类当中定义同名的虚函数,那么在子类虚函数表中就会写上父类的虚函数的函数入口地址;如果我们在子类当中也定义了虚函数,那么在子类的虚函数表中我们就会把原来的父类的虚函数的函数地址覆盖一下,覆盖成子类的虚函数的函数地址,这种情况就称之为函数的覆盖。
查看全部 -
含有纯虚函数的类叫抽象类,除了纯虚函数还可以有其他成员函数。接口类就是只有纯虚函数的类,不包含其他非纯虚函数。
用接口类指针作为函数参数才能体现多态的优势
查看全部 -
首先flymatch函数不仅可以传入指针,还可以传入实例化对象。
1、课程中为什么要传入flyable指针呢,是基于基类的引用和指针可以引用子类的对象这一原则,也就是说flyable *p 这个指针既可以引用bird也可以引用plan,这样做的好处后面会说到。
2、那为什么不用flyable p 呢,是因为flyable是纯虚函数,flyable p就是实例化一个flyable的对象p。显而易见,纯虚函数是不能被实例化的,所以flyable p 是非法的。
3、那可以传入什么样的实例化对象呢,bird和plan的实例化对象都可以。但是一旦这样做了就会有一个限制,如果函数中声明的是一个bird的对象,那么这个函数就不能再调用plan的对象了,因为bird和plan是两个完全不同的类,所以函数只能接受bird的对象而不能接受plan的对象,就像一个人如果是男人,那么他肯定不可能是女人。如果想处理plan的对象,那么只能再创建另外一个函数来处理了。
4、事实都不是绝对的,人妖的出现证明了一个人即可以是男人也可以是女人,而flayable *p的出现呢,就是为了让函数即可以调用bird的对象又可以调用plan的对象以及其它flyable的子类对象,这种编程灵活性是所有编程人员所崇尚的。
讲解完毕,如果帮到你请给个好评吧!媳妇非得要那个慕课君抱枕,拼命赚积分ing。。。。。。
总结:基类指针可以调用子类函数 父类有多个子类,那他就可以几个都调用了。
接口类:只有纯虚函数,无其他函数、数据成员,子类继承父类纯需,还需再定义为虚函数,--因为在父类中没有实现,需要在子类中实现。
查看全部 -
纯虚函数:比如说定义一个人类,不知道他具体干嘛,这时候就可以定义一个抽象类的work--》纯虚函数
只在类中声明,不定义;
纯虚函数不能实例化对象,但可以指向子类地址;
查看全部 -
异常处理
抛出异常:throw
捕获异常:try{}catch(){}
void test1() { if() { throw new IndexException(); } }
int main() { try { test1(); test2(); } catch(int){cout<<"error:int"<<endl;} catch(double &e){cout<<"e="<<e<<endl;} catch(Exception &e){e.printException;} catch(...){cout<<"error..."<<endl;} system("pause"); return 0; }
(1)throw 时可以定义:throw string("blablabla"); throw 时可以动态申请内存:throw new IndexException,或者throw IndexException()
注意:throw IndexException() 可以类比new IndexException() 实际上前者是抛出一个类,后者是申请一个类。 从老师的视频中可以看到,该类是使用默认构造函数, 即没有初始化参数。所以IndexException后加(),内无参数,如果有参数,那么()内应该填写相应初始化参数值。
(2) catch(异常父类 &e)比较常用,这样就可以调用子类的相关函数,如打印对应异常信息。也可以catch(类型),catch(类型 &e)。catch(...)是最后的处理。
(3) 如果test1()抛出异常,直接跳到外部函数的catch(){}捕获异常,结束调用函数,也不再执行test2(),并结束程序。
(4) 常见的异常:数据下标越界,除数为零,内存不足
查看全部 -
RTTI 运行时类型识别(Run-Time Type Identification )
有的编译器需要手动包含头文件#include <typeinfo>
在父类指针指向子类对象时,为了能够调用子类对象的非虚函数,引入RTTI,进行对象类型识别与对象指针的转换,从而。。。
1、typeid注意事项:
(1)type_id返回一个type_info对象的引用
(2)如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数
(3)只能获取对象的实际类型
Flyable *p=new Bird();
cout<<type(p).name()<<endl; 打印出变量p的类型:class Flyable * cout<<tupe(*p).name()<<endl; 打印出*p的类型:class Bird
2、dynamic_cast注意事项:
(1)只能用于指针和引用的转换
(2)要转换的类型必须有虚函数,父类和子类都有虚函数表明是多态类
(3)转换成功返回子类地址,失败返回NULL
查看全部 -
重载,隐藏,覆盖的区别:
(1)重载是指同一类中两个同名函数,但是参数的类型和数量不同(静态多态)
(2)隐藏是指子类对象调用函数时,对父类同名函数的隐藏。具有同名函数,即使参数不同。 如果要调用父类的该函数需要:子类对象.父类::函数
(3)覆盖是指父类指针指向子类对象的情况下,通过父类指针调用虚函数时,子类同名虚函数对父类同名虚函数的覆盖。具有同名的虚函数,且参数与返回类型也相同。(动态多态)
注意:父类指针指向子类对象的情况下,如果父类与子类同名的成员函数不是虚函数,则只能调用父类的该函数,子类该函数和子类其他数据成员一样不同被父类指针调用。
查看全部 -
对象的大小
(1)类实例化对象时,对象的大小就是类中数据成员所占的内存大小(数据成员,不包含成员函数);
(2)若类中存在一个或多个虚函数或虚析构函数,则会在实例对象的前4个(字节Byte)内存单元存放一个虚函数表指针;
注意1:每个类只有一个虚函数表,所有该类的对象共用一张虚函数表。
注意2:父类和子类的虚函数表不同,但虚函数表中的函数地址可能相同,指向同一个虚函数地址(这种情况父类虚函数未被子类的覆盖, 没有形成多态)
(3)如果类中没有数据成员或虚函数,则对象大小为1字节,用来标识对象的存在。
查看全部 -
虚函数实现多态的原理:
虚函数表指针(指向了虚函数表的首地址)->虚函数表(地址的偏移找到对应的虚函数的地址)->虚函数。
在父类指针指向子类实例时:
(1)在子类中定义了同名的虚函数,就会在子类的虚函数列表中将父类中定义的虚函数的函数地址覆盖掉,从而实现多态。
(2) 虚析构函数的原理:通过父类类指针指向子类实例的内存空间,找到子类的虚函数表指针所指向的虚函数表,然后在表中找到虚析构函数地址,从而找到虚析构函数执行子类的虚析构函数,再自动执行父类的虚析构函数。
补充1:理论前提,执行完子类的析构函数后,会执行父类的析构函数.
补充2:C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。
比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议
查看全部 -
virtual的适用情形与使用限制
virtual适用的以下情形:
虚继承,class B:virtual public A (菱形继承A-B,C-D)避免重复继承数据
虚函数,virtual void fun() 实现多态,不同子类实例化的父类调用相同方法而结果不同。子类与父类的虚函数名和参数完全一致才能实现多态,只是函数内部实现方法不同。虚函数具有继承性,父类对应函数是虚函数,子类对应函数必然也是虚函数。
虚析构函数,virtual ~A( ) 防止释放子类实例化的父类中子类内存泄漏的问题(子类构造函数中在堆中用new申请了内存时引起的内存泄漏),最好析构前都加virtual
virtual的使用限制
1、普通函数不能是虚函数,也就是virtual只能修饰类的成员函数,不能修饰全局函数
2、静态成员函数不能是虚函数,因为静态成员函数是整个类的,而不是和某个对象共同存在的
3、内联函数不能是虚函数,如果修饰了内联函数,那么会忽略掉inline关键字
4、构造函数不能是虚函数
查看全部 -
当父类与子类之间有重名函数时:
(1)通过子类对象访问父类该重名函数(于是有了隐藏):
父类与子类有重名函数时,这时出现了隐藏,即子类是继承了父类的该重名函数的,但是将其隐藏了。通过子类对象访问该函数时,访问的是子类自己的该函数,如果想要访问父类的该函数,则要在函数前面加上父类的名称空间限定;代码示例如下:父类Person和子类Worker,都有一个同名函数name();通过子类对象worker调用父类该函数时,worker.Person::name();
(2)通过父类指针指向子类对象,并访问子类重名函数(于是有了虚函数):
父类指针指向子类对象时,父类指针只能访问子类对象的数据成员(部分,继承自父类的),和访问父类在代码区的自己的成员函数;此时,如果想要通过父类指针访问到子类对象的成员函数(即通过父类指针调用子类的析构函数或者普通重名函数),就要将该重名函数设定为虚函数,然后将虚函数地址放进该类的虚函数表中(于是有了覆盖,即在子类虚函数表中用子类虚函数地址覆盖掉父类虚函数地址),而对象又多了个数据成员(虚函数表指针),且在对象内存块首位;这样当我们用父类指针指向子类对象,且调用子类重名虚函数时,就要先在虚函数表中查找,如果找到,就执行;即调用成员函数时,有虚函数表先在虚函数表中查找,然后再在代码区查找;
这里我只解释了父类指针访问子类重名函数的情况,没有解释虚析构函数不重名也能访问的情况。我想可能是析构函数有自己特殊的用法吧,可能每个类的析构函数都是同一个名字,而在代码中名字是不同的吧。这里如果有同学想明白了,希望能不吝赐教;
(3)通过子类对象初始化父类对象,通过父类对象不能访问子类重名函数(虚):
按照上面的理论,子类对象初始化父类对象后,子类对象的数据成员会覆盖掉父类对象的数据成员,但是这里,父类原有的虚函数表是没有被覆盖掉的,父类对象的虚函数表指针还是自己的表指针;表指针里的虚函数地址还是父类自己的虚函数地址,所以此时通过父类对象只能访问到子类对象的数据成员(继承自父类的),访问自己代码区的成员函数和虚函数列表中的自己的虚函数,不能访问到子类虚函数列表中的虚函数;
查看全部 -
看到这了查看全部
-
用父类Exception就可以捕获到try中的子类对像,然后通过RTT处理
查看全部
举报