C++之构造函数为什么不是虚函数能为虚函数

C++之构造函数为什么不能为虚函数_百度知道
C++之构造函数为什么不能为虚函数
答题抽奖
首次认真答题后
即可获得3次抽奖机会,100%中奖。
采纳数:10
获赞数:24
擅长:暂未定制
首先,虚函数是为了实现多态,多态靠虚表指针实现。比如,子类继承父类,那么子类对象在构造时,在内存中插入虚表指针,虚表指针指向续表。后续虚函数的访问都是通过这个虚表指针。如果构造函数为虚函数,要通过虚表指针去访问,那么谁去完成 “构造时,在内存中插入虚表指针”的动作呢?所以构造函数不能为虚函数不知道说明白了没
忘采纳哈~~
为你推荐:
其他类似问题
您可能关注的内容
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。构造函数能为虚函数吗?
[问题点数:0分]
本版专家分:5
结帖率 100%
CSDN今日推荐
本版专家分:581
2000年 总版技术专家分年内排行榜第一
2000年11月 总版技术专家分月排行榜第一2000年10月 总版技术专家分月排行榜第一
2001年2月 Web 开发大版内专家分月排行榜第一2000年11月 Web 开发大版内专家分月排行榜第一2000年10月 Web 开发大版内专家分月排行榜第一2000年11月 VC/MFC大版内专家分月排行榜第一2000年10月 VC/MFC大版内专家分月排行榜第一2000年11月 VB大版内专家分月排行榜第一2000年10月 VB大版内专家分月排行榜第一
2000年12月 Web 开发大版内专家分月排行榜第二
本版专家分:7617
2001年9月 C/C++大版内专家分月排行榜第一2001年8月 C/C++大版内专家分月排行榜第一
2001年7月 C/C++大版内专家分月排行榜第二
本版专家分:5
本版专家分:581
2000年 总版技术专家分年内排行榜第一
2000年11月 总版技术专家分月排行榜第一2000年10月 总版技术专家分月排行榜第一
2001年2月 Web 开发大版内专家分月排行榜第一2000年11月 Web 开发大版内专家分月排行榜第一2000年10月 Web 开发大版内专家分月排行榜第一2000年11月 VC/MFC大版内专家分月排行榜第一2000年10月 VC/MFC大版内专家分月排行榜第一2000年11月 VB大版内专家分月排行榜第一2000年10月 VB大版内专家分月排行榜第一
2000年12月 Web 开发大版内专家分月排行榜第二
本版专家分:55
本版专家分:15
本版专家分:0
本版专家分:1415
2000年1月 C/C++大版内专家分月排行榜第一
本版专家分:31
本版专家分:218
本版专家分:4081
本版专家分:182
本版专家分:128
本版专家分:30
匿名用户不能发表回复!
其他相关推荐C++中,&为什么需要定义析构函数为虚函数
先构造一个类,如下所示:
上面代码在运行的时候,生成Derive的对象的时候调用的构造函数,会首先调用基类的构造函数,所以当函数执行完毕,撤销s
的时候,也会调用Derive的析构函数之后调用Base
的析构函数,也就是说,不管析构函数是不虚函数,派生类对象在撤销的时候,肯定会一次调用基类的析构函数。那我们为什么要将析构函数定义为虚函数呢?
&&原因是因为多态的存在。
&我们大家都知道,在C++
中,当一个对象销毁时,析构函数是用来对类对象和对象成员进行释放内存和做一些其他的cleanup操作。析构函数靠~符号来区分,~ 出现在
析构函数名字的前面, &当我们去定义一个 虚析构函数时,你只需要简单的的在~符号前面 加一个
&virtual标志就可以了。
&为什么需要将析构函数声明为 虚函数,我们最好用几个例子来验证一下,我们首先以一个
不使用虚析构函数的例子开始,然后我们使用一个使用析构函数的例子。一旦看到了其中的区别,你就会明白
为什么需要将析构函数声明为虚函数。让我们开始看一下代码。
& & &Example
&without a Virtual Destructor:
输出 是下面这样的:
<img src="http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif" real_src ="http://img.blog.csdn.net/37943" ALT="" STYLE="border-style:"
TITLE="C++中,&为什么需要定义析构函数为虚函数" />
根据上面的输出,我们可以看到当我们新建一个指向Deriver类型对象指针的时候,构造函数按照正适当的顺序依次调用,但是这里有一个主要问题
当我们删除指向Deriver 的基类指针时,
Deriver类中的析构函数没有调用。这里调用的是Base的析构函数(静态联编)这里设计
那何为动态联编和
&静态联编?下面讲解一下:
联编是一个计算机程序彼此相关连的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。
&&将源代码中的函数调用解释为执行特定的函数代码被称为函数名联编(binding).
&在编译过程中进行联编被称为静态联编(static binding),又称早期联编(early
binding).它对函数的选择是根据基于对象的 指针或者引用来确定的。(即指针或者引用
指向哪个对象就调用哪个对象的相应的函数)
&编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编(dynamic
binding), 又称为晚期联编(late binding).
&1.为什么有两种类型的联编以及为什么默认为静态联编?
&&&&&&如果动态联编让您能够重新定义类方法,而静态联编在这方面很差,为何摒弃静态联编呢?原因有两个-----效率
&&&&&&首先来看效率.为使程序能够在运行阶段进行决策,必须采取一些方法来跟踪实际运行中基类指针或引用指向的对象类型,这增加了额外的开销.例如,如果类不会用作基类,则不需要动态联编.同样,如果派生类不重新定义基类的任何方法,也不需要使用动态联编.在这些情况下,使用静态联编更合理,效率也更高.由于静态联编的效率更高,因此被设置为C++的默认选择.Strousstrup说,C++的指导原则之一是,
不要为不使用的我付出代价(内存或者处理时间).仅当程序设计确实 需要虚函数时,才使用它们.
&&&&&&接下来看概念模型.在设计类时,可能包含一些不在派生类重新定义的成员函数.不将访函数设置为虚函数有两方面好处:首先效率高;其次,指出不要重新定义该函数.这表明,仅将那些预期将被重新定义的方法声明为虚拟的.
&&2.虚函数的工作原理
&&&&&&通常,编译器处理虚函数的方法是:给每一个对象添加一个隐藏成员.隐藏成员中保存了一个指向函数地址数组的指针.这种数组称为虚函数表(virtual
function table,
vtbl).虚函数表中存储了为类对象进行声明的虚函数的地址.例如,基类对象包含一个指针,访指针指向基类中所有虚函数地址表.派生类对象将包含一个指向独立地址表的指针.如果派生类提供了虚岁函数的新定义,访虚函数表将保存新函数的地址;如果派生类没有重新定义虚函数.该vtbl将保存函数原始版本的地址.如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中.注意,无论类中包含的虚函数是1还是10
个,都只需要在对象中添加1个地址成员,只是表的大小不同而已.
&&&&&&调用虚函数时,程序将查看存储在对象中的vtbl地址,然后转向相应的函数地址表.如果使用类声明中定义的第一个虚函数,则程序将使用数组中的第一个函数地址,并执行具有该地址的函数.如果使用类声明中的第三个虚函数,程序将使用地址为数组中第三个元素的函数.
&&&&&&简而言之,使用虚函数时,&在内存和执行速度方面有一定成本,包括:
&&&&&&&&&&&&每个对象都将增大,增大量为存储地址的空间.
&&&&&&&&&&&&对每个类,编译器都创建一个虚岁函数地址表.
&&&&&&&&&&&&每个函数调用都有需要执行一步额外的操作,即到表中查找地址.
&&&&&&虽然非虚函数的效率比虚函数稍高,但不具备动态联编功能.
那我们了解了动态联编和静态联编,如何解决这个问题呢,使用虚函数来
转到动态联编。
我们用基类指针 去指向一个派生类对象,然后释放该指针,在没有将基类析构函数声明为
虚函数的时候,只调用的基类的析构函数,而没有去调用派生类的析构函数。(因为这里是使用的基类指针)
如果我们修改一下代码,使用派生类指针,并且释放派生类指针的话结果:
<img src="http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif" real_src ="http://img.blog.csdn.net/26423" ALT="" STYLE="border-style:"
TITLE="C++中,&为什么需要定义析构函数为虚函数" />
这里会首先调用派生类析构函数,然后 在调用基类的析构函数。
解决: 将 基类析构函数生命为虚析构函数,
&Example with a Virtual
Destructor:
我们需要做的就是修改Base 类中的析构函数,在~前面加上virtual ,关键字为红色。
改变之后,运行输出为:
<img src="http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif" real_src ="http://img.blog.csdn.net/26423" ALT="" STYLE="border-style:"
TITLE="C++中,&为什么需要定义析构函数为虚函数" />
我们在基类中将析构函数标明为虚函数,就表示在使用析构函数时,是采用动态联编的。那么delete
basePtr的时候不再是采用静态联编直接在编译的时候确定basePtr指向的析构函数,而是在运行的时候根据
指向的类型来调用析构函数。(如有错误,请指出。谢谢~)
现在你知道我们为什么需要 虚析构函数和
他们是怎么工作的了把?
如果你在 派生类中 分配了
内存空间的话,没有将基类的析构函数声明为虚析构函数,很容易发生内存泄漏事件。
首先我们在基类,和派生类中都个分配了 10个
字节的空间,这些空间应该由程序员来释放,如果没有释放掉的话,就会造成内存泄漏。
来看一下运行结果:
<img src="http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif" real_src ="http://img.blog.csdn.net/01215" ALT="" STYLE="border-style:"
TITLE="C++中,&为什么需要定义析构函数为虚函数" />
我们可以看到只删除了基类的分配的空间,这个时候派生类的对象的空间没有删除,内存泄漏。
这一部分摘自:
另外一个例子:
这里CfunctionEx和Cfunction中本身并没有分配内存,应该不会有内存泄漏。和上例一样当删除pCFun时,它只调用了Cfunction的析构函数而没调用CfunctionEx的析构函数,但CfunctionEx本身并没分配内存。所以发生内存泄露的地方是m_cbase,因为它是CBase的实例且是CfunctionEx成员变量,当CfunctionEx的析构函数没有被调用时,当然m_cbase的析构函数也没有被调用,所以CBase中分配的内存被泄漏。
解决以上问题的方法很简单,就是使基类Cfunction的析构函数为虚函数就可以了。
这样就得出一个结论,当你的基类的析构函数不为虚的话,其子类中所有的成员变量的类中分配的内存也将可能泄漏。
这里说的可能是因为,如果程序中没有以上示例类似写法(指用基类指针指向子类实例,虚函数是C++的精华,很少有人不用的,由其是在大中型软件项目中),就不会出现本文所说的内存泄漏。看来在基类中使析构函数为虚函数是如此的重要。所以强烈建议在基类中把析构函数声明为虚函数,但是只有你写的类并不做为基类时例外。
将基类的析构函数设为virtual型,则所有的基类的派生类对象的析构函数都会自动设置为virtual型,这保证了任何情况下,不会出现由于析构函数没有被调用而导致的内存泄漏。
这是MFC将基类的析构函数设置为虚函数的真正原因。
为什么 构造函数不能为虚函数。原文地址:
& &1.从存储空间角度
& &虚函数对应一个vtale,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数,就需要到vtable中调用,可是对象还没有实例化,没有内存空间分配,如何调用。(悖论)
& 2.从使用角度
&&&&&&&&虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它。但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
3、从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数&&
&&从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数
4、当一个构造函数被调用时,它做的首要的事情之一是初始化它的V P T
R。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。&当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码-
-既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。
&&&所以它使用的V
P T R必须是对于这个类的V TA B L E。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, V P T R将
保持被初始化为指向这个V TA B L E, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置V P T R指向它的
V TA B L E,等.直到最后的构造函数结束。V P T
R的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。
&&&&&&&&但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置V
P T R指向它自己的 V TA B L E。如果函数调用使用虚机制,它将只产生通过它自己的V TA B L E的调用,而不是最后的V
TA B L E(所有构造函数被 调用后才会有最后的V TA B L E)。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。探讨C++中不能声明为虚函数的有哪些函数
&更新时间:日 09:01:08 & 投稿:jingxian
下面小编就为大家带来一篇探讨C++中不能声明为虚函数的有哪些函数。希望对大家有所帮助。一起跟随小编过来看看吧,祝大家游戏愉快哦
常见的不不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。
1.为什么C++不支持普通函数为虚函数?
普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。
多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层
函数不属于成员函数,是不能被继承的
2.为什么C++不支持构造函数为虚函数?
这个原因很简单,主要是从语义上考虑,所以不支持。因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。(这不就是典型的悖论)
(1)构造函数不能被继承,因而不能声明为virtual函数
(2)构造函数一般是用来初始化对象,只有在一个对象生成之后,才能发挥多态
作用,如果将构造函数声明为virtual函数,则表现为在对象还没有生成的情
况下酒使用了多态机制,因而是行不通的。
3.为什么C++不支持内联成员函数为虚函数?
其实很简单,那内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数)
inline函数和virtual函数有着本质的区别,inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数体现的是一种运行期机制。此外,一切virtual函数都不可能是inline函数。
4.为什么C++不支持静态成员函数为虚函数?
这也很简单,静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态邦定的必要性。不能被继承,只属于该类。
5.为什么C++不支持友元函数为虚函数?
因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。友元函数不属于类的成员函数,不能被继承。
* main.cpp
Author: china
#include &iostream&
cout && "基类构造" &&
/*在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。
* 你可以吧virtual去掉试一下
* 因此在类的继承体系中,基类的析构函数不声明为虚函数容易造成内存泄漏。所以如果你设计一定类可能是基类的话,必须要声明其为虚函数。
virtual ~B() {
cout && "基类析构" &&
virtual void func() {
cout && "基类的func()" &&
class D :public B{
cout && "派生类构造" &&
cout && "派生类析构" &&
void func() {
cout && "派生类的func()" &&
int main(int argc, char **argv) {
D //调用构造先有对象
p-&func(); //再体现多态机制
p = new D(); //再调用构造
p-&func(); //再体现多态机制
以上就是小编为大家带来的探讨C++中不能声明为虚函数的有哪些函数全部内容了,希望大家多多支持脚本之家~
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具C++中为什么一定要出现虚函数,用函数覆盖不可以解决问题么,换句话说,虚函数和函数覆盖有什么区别_百度知道
C++中为什么一定要出现虚函数,用函数覆盖不可以解决问题么,换句话说,虚函数和函数覆盖有什么区别
&#xe6b9;答题抽奖
首次认真答题后
即可获得3次抽奖机会,100%中奖。
lorddenton
来自科学教育类芝麻团
lorddenton
采纳数:170
获赞数:800
参与团队:
长篇大论这里就不说了,举个例子class fruit{public:
void func()
printf(&fruit&#92;n&);
virtual void vfunc()
printf(&v fruit&#92;n&);
}};class apple:public fruit{public:
void func()
printf(&apple&#92;n&);
void vfunc()
printf(&v apple&#92;n&);
}};int main(int argc, char* argv[]){
fruit* f = new apple(); //注意 f是父类指针
f-&func();
f-&vfunc(); return 0;} 结果是:fruitv apple例子中func采用了函数覆盖vfunc采用了虚函数当fruit类的指针指向一个apple类的对象时,func调用了父类的函数,vfunc调用了子类的函数。如过是函数覆盖,调用成员函数时,直接调用对象指针所属类的成员函数,例子中为fruit虚函数的情况下,调用成员函数时,调用的是指向对象的所属类的成员函数,例子中为apple如果不明白,可以追问。
小飞花儿的忧伤
小飞花儿的忧伤
采纳数:392
获赞数:1392
虚函数可以实现动态绑定,比如父类有个虚函数函数f和子类中重定义了fFather *p=new S则p-&f调用的就是子类的Father *p=new F则p-&f调用的是父类的函数f。对于p-&f在编译阶段是不知道要调用那个函数的,只有在执行阶段才能确定。而函数覆盖在编译阶段就确定了,父类就调用父类的,子类就调用子类的(父类的被覆盖了)。
采纳数:190
获赞数:455
虚函数的用途只体现在用指针构造函数的情况下比如 A B 两个类,B是A的子类,如果 A 和 B 都有一个函数叫做 C那么 A *a = new B;如果 C 不是虚函数的话调用 a-&C() 的话,调用的就是 A 的 C如果 C 是虚函数的话调用 a-&C() 的话,调用的就是 B 的 C虚函数就是为了起这个作用的而正常实例化的话,虚函数几乎不起作用我应该没说错。
采纳数:26
获赞数:72
简单点跟你说吧,虚函数就是用来被继承类重载的,重写这个函数一样.功能全变了.父类的这个虚方法功能不变.当然可以调用父类的.这是包含面相对象的思想的东西.编多了自能体会虚函数的好处.现在你把它但成一般函数对待就行了.
也许我是驴
也许我是驴
获赞数:29
擅长:暂未定制
使用虚函数可以像楼上说的那样动态绑定,函数覆盖的话就得声明不同的指针类型,去看下这里:
其他2条回答
为你推荐:
其他类似问题
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。}

我要回帖

更多关于 构造函数为什么不能是虚函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信