2017-6-18 希望找工作有用
1 多态性定义,作用.为什么会引入它。
答:当派生类被向上类型转换的指针指向,系统分不清传递过来的对象是基类还是子类,所以不知道调用哪个类对象的成员函数。为此,引入多态性技术。“一个接口,多个方法”
同一类族中不同类的对象,对同一函数调用作出不同的响应
在编译期间就确定哪个对象的成员函数被调用叫早绑定。
在运行期间,根据哪个类型确定成员函数叫晚期绑定。
作用:接口重用。(一定要分清重载和重写)
别人家的解释: 指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
a、编译时多态性:通过重载函数实现
b、运行时多态性:通过虚函数实现。
多态性的工作依赖虚函数定义。
2 虚函数的定义,作用,为什么会引入它。
虚函数:在基类用virtual声明成员函数为虚函数
作用:虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
如何使用:1 在基类用virtual声明成员函数为虚函数。
2 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。
3 定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。
4 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
虚函数只能借助于指针或者引用来达到多态的效果。
3 向上类型转换的定义,作用。
定义:子类向基类转换。
为什么呢,不知道。
4 捆绑定义,早捆绑,晚捆绑,为什么要实现捆绑。
早捆绑:编译器在编译时就知道一个变量的类型。逻辑元素与内存单元建立联系。
在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。
晚捆绑:在运行时才知道对象的类型。
5 虚函数的扩展性,
6 如何实现晚捆绑,存放型信息、功能演示。
实现:,典型的编译器对每个包含虚函数的类将创建一个表(VTABLE),在VTABLE中放着特定类的虚函数地址。在每个带有虚函数的类中,编译器会放置一个指针VPTR,指向这个对象的VTABLE。当通过基类指针做虚函数调用时,编译器静态的插入能取得这个VPTR并在VTAVLE表中查找函数地址的代码,这样就会引起晚捆绑的发生。
存放类型:多一个指针.vptr.
7 抽象类和纯虚函数。
纯虚函数: 纯虚函数是1、在基类中声明的虚函数,
2、它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。
3、在基类中实现纯虚函数的方法是在函数原型后加“=0”。
为什么会引入:复制来的:
1.因为当你顶级基类A有一虚函数play();,而A的子类B也有play();,A的子类C也有play();,这就说明如果顶级基类A中的play();若定义怎么玩是不合理的。因为B有B的玩法C有C的玩法。所以这种情况下定义为纯虚函数目的在于使子类仅仅只是继承函数的接口,告诉子类的设计者“你必须在子类中提供一个纯虚函数的实现,但我不知道你会怎么样实现它”
2.引入纯虚函数,则编译器要求在子类中必须予以重写以实现多态性
抽象类:1 含有纯虚函数的类称为抽象类,
2 它不能生成对象,
为什么:因为抽象类声明了纯虚函数,所以用户不能创建类的纯虚函数实例,只能创建它的派生类的实例
3 而且只能是基类。
作用:将有关的操作作为结果接口组织在一个继承层次结构中,由它为子类提供一个公共的根,子类将具体实现在其基类中作为接口的操作。
8 纯虚定义,为什么。
9 对象切片。
关于对象切片Thinking in C++中有这么一段话
英文原版:
If you upcast to an object instead of a pointer or reference,
something will happen that may surprise you: the object is “sliced”
until all that remains is the subobject that corresponds to the
destination type of your cast.
这句话的意思也就是说:在函数传参处理多态性时,如果一个派生类对象在UpCasting时,用的是传值的方式,而不是指针和引用,那么,这个派生类对象在UpCasting以后,将会被slice(切分)成基类对象,也就是说,派生类中独有的成员变量和方法都被slice掉了,值剩下和基类相同的成员变量和属性。这个派生类对象被切成了一个基类对象。
10 虚函数和构造函数之间的关系。
为什么构造函数不能为虚函数:
从使用上来说,虚函数是通过基类指针或引用来调用派生类的成员的,则在调用之前,对象必须存在,而构造函数是为了创建对象的。
1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。。。
2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。
更多:重复了,为了更好地理解。
1 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
2 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
3 构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
4 从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有必要成为虚函数。
5 当一个构造函数被调用时,它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码——既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。所以它使用的VPTR必须是对于这个类的VTABLE。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持被初始化为指向这个VTABLE, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置VPTR指向它的 VTABLE,等.直到最后的构造函数结束。VPTR的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生类顺序的另一个理由。但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置VPTR指向它自己的VTABLE。如果函数调用使用虚机制,它将只产生通过它自己的VTABLE的调用,而不是最后的VTABLE(所有构造函数被调用后才会有最后的VTABLE)。
虚析构函数:
如果,不把基类的析构函数设置为虚函数,则在删除对象时,如果直接删除基类指针,系统就只能调用基类析构函数,而不会调用派生类析构函数。这就会导致内存泄露。
如果,把基类的析构函数设置为虚函数,则在删除对象时,直接删除基类指针,系统会调用派生类析构函数,之后此派生类析构函数会引发系统自动调用自己的基类,这就不会导致内存泄露。
11 向下类型转换。
定义:基类向子类转换。
使用dynamic_cast进行向下强制类型转换。使用此关键字有一下几个条件
1.必须有虚函数
2.必须打开编译器的RTTI开关(vc6: progect-> settings -> c/c++ tab ->category[c++ language]-> Enable RTTI)
3.必须有继承关系
虚函数的定义要遵循以下重要规则:
1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。
2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。
3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。
4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义,但是在编译的时候系统仍然将它看做是非内联的。
5.构造函数不能是虚函数,因为构造的时候,对象还是一片未定型的空间,只有构造完成后,对象才是具体类的实例。
6.析构函数可以是虚函数,而且通常声名为虚函数。
7当一个类不打算作为基类时,不用将其中的函数设置为虚函数
1.纯虚函数没有定义
2.纯虚函数是用来规范子类的行为,即接口
3.抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用
4.定义纯虚函数就是为了让基类不能实例化,因为实例化并没有意义
5.纯虚函数的引入更加安全:因为它避免了不小心没有去实现的问题,它会提醒子类去做相应的实现
6.纯虚函数的引入更加有效率:编码的效率会提升!
7纯虚函数存在的一个重要意义:避免对象切片。