为什么要用纯虚函数数,普通函数不行吗

C、C++(86)
& & & & & & & & & & & & & & & & & & & & & & & & &&什么函数不能声明为虚函数?
一个类中将所有的成员函数都尽可能地设置为虚函数总是有益的。&
但设置虚函数须注意:&
1:只有类的成员函数才能说明为虚函数;&
原因:普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。
2:静态成员函数不能是虚函数;
&因为static属于class自己的,也必须有实体;&没有this指针,它无法进行对象的判别。
3:内联函数不能为虚函数;
原因:&inline是编译时展开,必须有实体;内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数)
4:构造函数不能是虚函数;&
1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,可是这个指向vtable的指针其实是存储在对象的内存空间的。如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
2. 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
3. 构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
4. 从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有必要成为虚函数。
5. 当一个构造函数被调用时,它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码——既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。所以它使用的VPTR必须是对于这个类的VTABLE。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持被初始化为指向这个VTABLE, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置VPTR指向它的
VTABLE,等.直到最后的构造函数结束。VPTR的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生类顺序的另一个理由。但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置VPTR指向它自己的VTABLE。如果函数调用使用虚机制,它将只产生通过它自己的VTABLE的调用,而不是最后的VTABLE(所有构造函数被调用后才会有最后的VTABLE)。
5:析构函数可以是虚函数,而且通常声明为虚函数。
原因:析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。构造一个CStudent的动态对象如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全。类析构函数要声明为虚函数这样派生类调用析构函数才能层层回调,释放资源。这也是虚函数的作用--提供回调的指针。
C++中,哪些函数不可以被声明为虚函数
总的来说,共有五种,普通函数(非成员函数)、构造函数、内联函数、静态函数、友元函数。
首先说明两点:
(1)虚函数是为了实现多态,而多态是属于动态联编,在运行时确定调用哪个函数。
(2)虚函数调用时,类之间需要有公有继承&+继承关系&+&基类指针或引用调用。
(1)普通函数为啥不能是虚函数?
原因:多态是依托于类的,要声明的多态的函数前提必须是虚函数。
(2)构造函数为啥不能是虚函数?
原因:多态是依托于类的,多态的使用必须是在类创建以后,而构造函数是用来创建构造函数的,所以不行。
具体的原因:虚表指针的初始化时在构造函数进行的,而虚函数需要放到虚表中。在调用虚函数前,必须首先知道虚表指针,此时矛盾就出来了。
(3)内联函数为啥不能是虚函数?
原因:内联函数属于静态联编,即内联函数是在编译期间直接展开,可以减少函数调用的花销,即是编译阶段就确定调用哪个函数了。但是虚函数是属于动态联编,即是在运行时才确定调用哪一个函数。显然这两个是冲突的。
(4)静态函数为啥不能使虚函数?
&1&从技术层面上说,静态函数的调用不需要传递this指针。但是虚函数的调用需要this指针,来找到虚函数表。相互矛盾
&2&从存在的意义上说,静态函数的存在时为了让所有类共享。可以在对象产生之前执行一些操作。与虚函数的作用不是一路的。
(5)友元函数为啥不能是虚函数?
原因:C++不支持友元函数的继承,不能继承的函数指定不是虚函数。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:176127次
积分:6784
积分:6784
排名:第2752名
原创:247篇
转载:65篇
评论:63条
(2)(10)(13)(12)(4)(8)(2)(5)(5)(17)(1)(3)(4)(9)(5)(6)(40)(112)(24)(33)TA的推荐TA的最新馆藏[转]&1488人阅读
C/C++(15)
如果子类覆盖的父类的虚函数
则父类的指针或引用实际指向子类的对象
那通过该指针或引用调用虚函数时,则将调用子类的(虚)函数
如果不是虚函数,则将调用父类的函数
虚函数的优势在于
当用指针调用虚函数的时候,即使是基类类型的指针,也会调用到派生类型的函数。& 我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明:&&&&&&& 有下面的两个类:class&ClxBase{public:&&&&ClxBase()&{};&&&&virtual&~ClxBase()&{};&&&&virtual&void&DoSomething()&{&cout&&&&&Do&something&in&class&ClxBase!&&&&&&};};class&ClxDerived&:&public&ClxBase{public:&&&&ClxDerived()&{};&&&&~ClxDerived()&{&cout&&&&&Output&from&the&destructor&of&class&ClxDerived!&&&&&&};&&&&&void&DoSomething()&{&cout&&&&&Do&something&in&class&ClxDerived!&&&&&&};};&&& 代码ClxBase&*pTest&=&new&ClxDpTest-&DoSomething();delete&pT&&& 的输出结果是:Do something in class ClxDerived!Output from the destructor of class ClxDerived!&&&&这个很简单,非常好理解。&&& 但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:Do something in class ClxDerived!&&& 也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。&&&&所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。&&&&当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:433539次
积分:5951
积分:5951
排名:第3456名
原创:152篇
转载:110篇
评论:38条
(1)(3)(11)(1)(1)(4)(3)(2)(1)(5)(12)(18)(1)(2)(1)(21)(19)(9)(67)(30)(6)(4)(12)(2)(5)(5)(14)(1)(1)TA的最新馆藏[转]&[转]&

我要回帖

更多关于 纯虚函数 的文章

 

随机推荐