-
纯虚函数:virtual int func() = 0;不需要去实现函数的时候用。 纯虚函数在虚函数列表中存储的值就是0,他并没有存储一个具有实际意义的指针。
含有纯虚函数的类叫抽象类。哪怕只含有一个纯虚函数也叫抽象类。
抽象类不允许实例化对象。
抽象类的子类也可以是抽象类。
抽象类的子类只有把抽象类当中的所有纯虚函数都做了实现,这个子类才可以实例化对象。
查看全部 -
当类中没有虚函数的时候:
我们通过类的对象访问其数据成员,访问其成员函数;前面已经知道,实例化对象时,只有数据成员的大小,这说明类的成员函数是统一放在内存代码区的,并且有自己的类名称空间作为限制,只能自己实例化的对象访问;所以我们通过对象访问数据成员,是直接在对象的内存块中偏移得到;而通过对象访问成员函数,则是要到代码区才能访问到;
当父类与子类之间有重名函数时:
(1)通过子类对象访问父类该重名函数(于是有了隐藏):
父类与子类有重名函数时,这时出现了隐藏,即子类是继承了父类的该重名函数的,但是将其隐藏了。通过子类对象访问该函数时,访问的是子类自己的该函数,如果想要访问父类的该函数,则要在函数前面加上父类的名称空间限定;代码示例如下:父类Person和子类Worker,都有一个同名函数name();通过子类对象worker调用父类该函数时,worker.Person::name();
(2)通过父类指针指向子类对象,并访问子类重名函数(于是有了虚函数):
父类指针指向子类对象时,父类指针只能访问子类对象的数据成员(部分,继承自父类的),和访问父类在代码区的自己的成员函数;此时,如果想要通过父类指针访问到子类对象的成员函数(即通过父类指针调用子类的析构函数或者普通重名函数),就要将该重名函数设定为虚函数,然后将虚函数地址放进该类的虚函数表中(于是有了覆盖,即在子类虚函数表中用子类虚函数地址覆盖掉父类虚函数地址),而对象又多了个数据成员(虚函数表指针),且在对象内存块首位;这样当我们用父类指针指向子类对象,且调用子类重名虚函数时,就要先在虚函数表中查找,如果找到,就执行;即调用成员函数时,有虚函数表先在虚函数表中查找,然后再在代码区查找;
这里我只解释了父类指针访问子类重名函数的情况,没有解释虚析构函数不重名也能访问的情况。我想可能是析构函数有自己特殊的用法吧,可能每个类的析构函数都是同一个名字,而在代码中名字是不同的吧。这里如果有同学想明白了,希望能不吝赐教;
(3)通过子类对象初始化父类对象,通过父类对象不能访问子类重名函数(虚):
按照上面的理论,子类对象初始化父类对象后,子类对象的数据成员会覆盖掉父类对象的数据成员,但是这里,父类原有的虚函数表是没有被覆盖掉的,父类对象的虚函数表指针还是自己的表指针;表指针里的虚函数地址还是父类自己的虚函数地址,所以此时通过父类对象只能访问到子类对象的数据成员(继承自父类的),访问自己代码区的成员函数和虚函数列表中的自己的虚函数,不能访问到子类虚函数列表中的虚函数;
查看全部 -
对象的大小
(1)类实例化对象时,对象的大小就是类中数据成员所占的内存大小(数据成员,不包含成员函数);
(2)若类中存在一个或多个虚函数或虚析构函数,则会在实例对象的前4个B内存单元存放一个虚函数表指针(指针大小为4B);
注意1:每个类只有一个虚函数表,所有该类的对象共用一张虚函数表。
注意2:父类和子类的虚函数表不同,但虚函数表中的函数地址可能相同,指向同一个虚函数地址(这种情况父类虚函数未被子类的覆盖, 没有形成多态)
(3)如果类中没有数据成员或虚函数,则对象大小为1字节,用来标识对象的存在。
查看全部 -
没有数据成员的类,实例化以后,sizeof后的值是1,原因是C++对于没有数据成员的对象,C++会用1个内存单元用来标记这个对象
查看全部 -
对象的大小:类实例化的对象,数据成员占据的内存大小,成员函数不占据内存。
对象的地址:实例化的对象,所占内存的第一个存储单元的地址
对象成员地址:实例化对象有一个或多个数据成员,每个数据成员都有自己的地址,占一定的内存空间(数据类型不同,地址不同)
虚函数表指针:具有虚函数的类实例化的对象时,对象的第一块内存中存储的就是虚函数表指针
查看全部 -
通过虚析构函数,释放父类指针就能够释放子类申请的内存,有个理论前提就是:执行完子类的虚构函数就会执行父类的析构函数。因此只要保证能够执行子类的虚构函数即可(就相当于子类的和父类的都释放了。 当然,只有在堆内申请内存才会出现要释放内存的过程。)
父类指针指向子类的时候,访问子类的虚函数列表,可以找到子类的虚析构函数,因此释放父类指针可以执行子类的虚析构函数。反言之, 如果父类和子类的不是虚析构函数,则析构函数不会存在与虚函数列表中,父类指针就找不到子类的析构函数。
查看全部 -
函数的隐藏:定义了父类与子类,且出现了同名的函数,称为隐藏
函数的覆盖:若在子类中未定义同名的虚函数,则子类的虚函数表中写上父类虚函数的入口地址;若在子类中定义了同名的虚函数,则在子类的虚函数表中,子类虚函数入口地址覆盖原来父类的
查看全部 -
虚函数:假设存在、作为基类方便不同子类派生函数的存在函数。其特点在于,虚函数本身没有任何意义,其只是作为一个象征:我所有的子类都有如此一个函数存在,至于子类的函数到底如何实现,基类并不知晓。
查看全部 -
函数指针的实质是指向这段函数代码的开头位置
查看全部 -
virtual在函数中的使用限制:
·普通函数不能是虚函数(编译错误)
· 静态成员函数不能是虚函数(编译错误)
·内联函数不能是虚函数(可编译,编译器忽略inline关键字是函数变为纯粹虚函数)
·构造函数不能是虚函数(编译错误)
查看全部 -
查看全部
-
多态分为两种 静态多态和动态多态 静态指的就是函数重载(是在一个函数或者一个类中 ) ;而动态多态就是 指的多个类之间的多态
查看全部 -
使用父类指针指向子类对象时,调用子类完全相同的函数时候 会执行父类函数,需要在父类加virtual关键字使父类函数成为虚函数,才能调用子类自己的同名函数
查看全部 -
静态多态(早绑定):成员函数同名,但参数数目不同时,调用成员函数使用的参数个数决定了启用不同的成员函数。 /*函数编译阶段就已经确定用哪种行为*/
动态多态(晚绑定):以封装和继承为基础,当父类指针指向子类类型时,要用virtual去修饰子类中已经重定义的成员函数(虚函数),否则使用的是父类的定义的成员函数;子类中的同名成员函数virtual可加可不加,系统默认补全。
”父类指针指向子类对象,通过父类指针只能寻址到从父类继承到的成员函数与数据成员,子类扩展的将会被截断。但是,如果将父类中同名函数设置为虚函数,创建子类对象时将会创建子类自有的【虚函数表】,虚函数表指向的成员函数将覆盖父类中的同名函数。“
查看全部 -
对不同对象下达相同指令,但是做的不同的操作
查看全部
举报