什么是C++虚函数有什么作用,虚函数有什么作用的作用和使用方

他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)博客访问: 206583
博文数量: 84
博客积分: 1850
博客等级: 上尉
技术积分: 802
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: C/C++ 11:43:51
1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称 $2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用 $只有声明而没有定义。3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。5. 虚函数的定义形式:virtual & &{method body}&& &纯虚函数的定义形式:virtual & &{ } = 0;&& &在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-ti$6. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类 $以下为一个简单的虚函数和纯虚寒数的使用演示!
#include<iostream>
using namespace std;
//father class
class Virtualbase
&& & & &&virtual
Demon()= 0;
//prue virtual function
&& & & &&virtual
Base() {cout<<"this is farther class"<<endl;}}//sub class
class SubVirtual
:public Virtualbase
&&&&void Demon()
&& & & & &cout<<" this is SubVirtual!" << endl;
&&&&void Base()
&& & & &&cout<<"this is subclass Base"<<endl;
instance class and sample
int main()
&& & & &Virtualbase* inst = new SubVirtual(); //multstate pointer
&& & & &inst->Demon();
&& & & &inst->Base();
&&&&inst = new Virtualbase();
inst->Base()
&& & &&return 0;
阅读(8621) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
请登录后评论。C++纯虚函数_百度百科
清除历史记录关闭
声明:百科词条人人可编辑,词条创建和修改均免费,绝不存在官方及代理商付费代编,请勿上当受骗。
C++纯虚函数
本词条缺少名片图,补充相关内容使词条更完整,还能快速升级,赶紧来吧!
为我们提供了一种语法结构,通过它可以指明一个只是提供了一个可被子类型改写的接口。但是,它本身并不能通过虚拟机制被调用,这就是纯(pure virtual function)。
C++纯虚函数C++纯虚函数
C++纯虚函数定义
纯虚函数是在中声明的,它可以在基类中有定义,而且必须定义自己的实现方法。基类不能生成对象,可以使用指针或者引用派生类对象。基类不在基类中实现纯虚函数的方法是在后加“=0”
virtual void funtion1()=0
C++纯虚函数引入原因
1、为了方便使用特性,我们常常需要在中定义。
2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则要求在中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为,它不能生成对象。这样就很好地解决了上述两个问题。
C++纯虚函数相似概念
指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时,。
a.编译时多态性:通过实现
b 运行时多态性:通过实现。
虚函数是在中被声明为virtual,并在中重新定义的成员函数,可实现成员函数的动态
包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。
C++纯虚函数程序举例
void f1();
virtual void f2();
virtual void f3()=0;
virtual ~A();
class B : public A
void f1();
void f2();
void f3();
virtual ~B();
int main(int argc, char* argv[])
A *m_j=new B();
m_j-&f1();
m_j-&f2();
m_j-&f3();
delete m_j;
f1()是一个普通的.
调用m_j-&f1();会去调用A类中的f1(),它是在我们写好代码的时候就会定好的.
也就是根据它是由A类定义的,这样就调用这个类的函数.
调用m_j-&f2();会调用m_j中到底保存的对象中,对应的这个函数.这是由于new的B对象.
f3()与f2()一样,只是在中不需要写函数实现.
清除历史记录关闭C++ 中的虚函数作用是什么?你知道应该注意什么吗?_C语言Plus_知道日报_百度知道
C++ 中的虚函数作用是什么?你知道应该注意什么吗?
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。言归正传,让我们一起进入虚函数的世界。虚函数表对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。 没关系,下面就是实际的例子,相信聪明的你一看就明白了。假设我们有这样的一个类:classBase{public:virtual voidf(){cout && &Base::f& &&}virtual voidg(){cout && &Base::g& &&}virtual voidh(){cout && &Base::h& &&}};按照上面的说法,我们可以通过Base的实例来得到虚函数表。 下面是实际例程:typedefvoid(*Fun)(void);BFun pFun = NULL;cout && &虚函数表地址:& && (int*)(&b) &&cout && &虚函数表 — 第一个函数地址:& && (int*)*(int*)(&b) &&/*这里的一点争议的个人看法*/原文认为(int*)(&b)是虚表的地址,而很多网友都说,(包括我也认为):(int *)*(int*)(&b)才是虚表地址而(int*)*((int*)*(int*)(&b)); 才是虚表第一个虚函数的地址。其实看后面的调用pFun = (Fun)*((int*)*(int*)(&b)); 就可以看出,*((int*)*(int*)(&b));转成函数指针给pFun,然后正确的调用到了虚函数virtual void f()。// Invoke the first virtual functionpFun = (Fun)*((int*)*(int*)(&b));pFun();实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)虚函数表地址:0012FED4虚函数表 — 第一个函数地址:Base::f通过这个示例,我们可以看到,我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调用Base::g()和Base::h(),其代码如下:(Fun)*((int*)*(int*)(&b)+0);// Base::f()(Fun)*((int*)*(int*)(&b)+1);// Base::g()(Fun)*((int*)*(int*)(&b)+2);// Base::h()这个时候你应该懂了吧。什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让我画个图解释一下。如下所示:注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。下面,我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。最后想要更深入学习的可以加下我自己建的C/C++学习交流群,欢迎喜欢C/C++的各位大神小神前来学习交流,每天晚上群里面也会有大佬直播企业项目开发学习,更有各种项目案例供你自己学习哦一般继承(无虚函数覆盖)下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:请注意,在这个继承关系中,子类没有重载任何父类的函数。对于实例:D 的虚函数表如下:我们可以看到下面几点:1)虚函数按照其声明顺序放于表中。2)父类的虚函数在子类的虚函数前面。我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。一般继承(有虚函数覆盖)覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:我们从表中可以看到下面几点,1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。2)没有被覆盖的函数依旧。这样,我们就可以看到对于下面这样的程序,Base *b = newDerive();b-&f();由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。多重继承(无虚函数覆盖)下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。对于子类实例中的虚函数表,是下面这个样子:我们可以看到:1) 每个父类都有自己的虚表。2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。多重继承(有虚函数覆盖)下面我们再来看看,如果发生虚函数覆盖的情况。下图中,我们在子类中覆盖了父类的f()函数。下面是对于子类实例中的虚函数表的图:我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:DBase1 *b1 = &d;Base2 *b2 = &d;Base3 *b3 = &d;b1-&f();//Derive::f()b2-&f();//Derive::f()b3-&f();//Derive::f()b1-&g();//Base1::g()b2-&g();//Base2::g()b3-&g();//Base3::g()安全性每次写C++的文章,总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述,相信我们对虚函数表有一个比较细致的了解了。水可载舟,亦可覆舟。下面,让我们来看看我们可以用虚函数表来干点什么坏事吧。一、通过父类型的指针访问子类自己的虚函数我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:Base1 *b1 = newDerive();b1-&f1();//编译出错任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)二、访问 non-public 的虚函数另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。如:classBase{private:virtual voidf(){cout && &Base::f& &&}};classDerive : publicBase{};typedefvoid(*Fun)(void);voidmain(){DFun pFun = (Fun)*((int*)*(int*)(&d)+0);pFun();}( 1 )virtual 虚函数先看一段简单代码:Code Segment:#include&stdio.h&classBase{public:virtual void__stdcall Output(){printf(&Class Base\n&);}};classDerive :publicBase{public:void__stdcall Output(){printf(&Class Derive\n&);}};voidTest(Base *p){p-&Output();}int__cdecl main(intargc,char* argv[]){DTest(&obj);return0;}基类的“Output”函数是个虚函数。那么,很明显地,程序的运行结果将是:( 2) virtual function table 虚函数表先来分析我们的main函数中的Derive类的对象obj,看看它的内存布局,由于没有数据成员,它的大小为4个字节,只有一个vfptr,所以obj的地址也就是vfptr的地址了。对一个C++类,如果它要呈现多态(一般的编译器会将这个类以及它的基类中是否存在virtual关键字作为这个类是否要多态),那么类会有一个virtual function table,而每一个实例(对象)都会有一个virtual function pointer(以下简称vfptr)指向该类的virtual function table的起始地址,而virtual function table表格地址所对应的内存单元的内容就是虚函数地址(其实并不是真正的函数地址,而是跳转到函数的jmp指令的地址)。( 2 ) 实现 virtual 功能结束语C++这门语言是一门Magic的语言,对于程序员来说,我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言,我们就必需要了解C++里面的那些东西,需要去了解C++中那些危险的东西。不然,这是一种搬起石头砸自己脚的编程语言。
图片文字版权所有,严禁转载!商业合作及授权请联系,百度知道日报保留一切法律权利。
文章出处:百度知道日报()
原始链接:
为您推荐:
【招募】百度派写手团纳新,就等有才的你!
热门作者推荐
服务于各行业品牌与产品的新闻营销。
探探健康生活
高级文物艺术品鉴定评估师(综合类) 双重学位
科技译者,为多家知名网站供稿。
致力于肾病知识普及,肾病营养宣教,肾病饮食咨询,健康资讯分享
最有趣的历史漫画!
合作及供稿请联系:C++&虚函数用法
C++中虚函数用法
虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。假设我们有下面的类层次:
virtual void foo() { cout
&& "A::foo() is called"
class B: public A
virtual void foo() { cout
&& "B::foo() is called"
那么,在使用的时候,我们可以:
A * a = new B();
a-&foo(); //
在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!
这个例子是虚函数的一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
虚函数只能借助于指针或者引用来达到多态的效果,如果是下面这样的代码,则虽然是虚函数,但它不是多态的:
virtual void foo();
class B: public A
virtual void foo();
void bar()
a.foo(); // A::foo()被调用
在了解了虚函数的意思之后,再考虑什么是多态就很容易了。仍然针对上面的类层次,但是使用的方法变的复杂了一些:
void bar(A * a)
a-&foo(); // 被调用的是A::foo()
还是B::foo()?
因为foo()是个虚函数,所以在bar这个函数中,只根据这段代码,无从确定这里被调用的是A::foo()还是B::foo(),但是可以肯定的说:如果a指向的是A类的实例,则A::foo()被调用,如果a指向的是B类的实例,则B::foo()被调用。
这种同一代码可以产生不同效果的特点,被称为“多态”。
1.2 多态有什么用?
多态这么神奇,但是能用来做什么呢?这个命题我难以用一两句话概括,一般的C++教程(或者其它面向对象语言的教程)都用一个画图的例子来展示多态的用途,我就不再重复这个例子了,如果你不知道这个例子,随便找本书应该都有介绍。我试图从一个抽象的角度描述一下,回头再结合那个画图的例子,也许你就更容易理解。
在面向对象的编程中,首先会针对数据进行抽象(确定基类)和继承(确定派生类),构成类层次。这个类层次的使用者在使用它们的时候,如果仍然在需要基类的时候写针对基类的代码,在需要派生类的时候写针对派生类的代码,就等于类层次完全暴露在使用者面前。如果这个类层次有任何的改变(增加了新类),都需要使用者“知道”(针对新类写代码)。这样就增加了类层次与其使用者之间的耦合,有人把这种情况列为程序中的“bad
smell”之一。
多态可以使程序员脱离这种窘境。再回头看看1.1中的例子,bar()作为A-B这个类层次的使用者,它并不知道这个类层次中有多少个类,每个类都叫什么,但是一样可以很好的工作,当有一个C类从A类派生出来后,bar()也不需要“知道”(修改)。这完全归功于多态--编译器针对虚函数产生了可以在运行时刻确定被调用函数的代码。
1.3 如何“动态联编”
编译器是如何针对虚函数产生可以再运行时刻确定被调用函数的代码呢?也就是说,虚函数实际上是如何被编译器处理的呢?Lippman在深度探索C++对象模型[1]中的不同章节讲到了几种方式,这里把“标准的”方式简单介绍一下。
我所说的“标准”方式,也就是所谓的“VTABLE”机制。编译器发现一个类中有被声明为virtual的函数,就会为其搞一个虚函数表,也就是VTABLE。VTABLE实际上是一个函数指针的数组,每个虚函数占用这个数组的一个slot。一个类只有一个VTABLE,不管它有多少个实例。派生类有自己的VTABLE,但是派生类的VTABLE与基类的VTABLE有相同的函数排列顺序,同名的虚函数被放在两个数组的相同位置上。在创建类实例的时候,编译器还会在每个实例的内存布局中增加一个vptr字段,该字段指向本类的VTABLE。通过这些手段,编译器在看到一个虚函数调用的时候,就会将这个调用改写,针对1.1中的例子:
void bar(A * a)
会被改写为:
void bar(A * a)
(a-&vptr[1])();
因为派生类和基类的foo()函数具有相同的VTABLE索引,而他们的vptr又指向不同的VTABLE,因此通过这样的方法可以在运行时刻决定调用哪个foo()函数。
虽然实际情况远非这么简单,但是基本原理大致如此。
1.4 overload和override
虚函数总是在派生类中被改写,这种改写被称为“override”。我经常混淆“overload”和“override”这两个单词。但是随着各类C++的书越来越多,后来的程序员也许不会再犯我犯过的错误了。但是我打算澄清一下:
override是指派生类重写基类的虚函数,就象我们前面B类中重写了A类中的foo()函数。重写的函数必须有一致的参数表和返回值(C++标准允许返回值不同的情况,这个我会在“语法”部分简单介绍,但是很少编译器支持这个feature)。这个单词好象一直没有什么合适的中文词汇来对应,有人译为“覆盖”,还贴切一些。
overload约定成俗的被翻译为“重载”。是指编写一个与已有函数同名但是参数表不同的函数。例如一个函数即可以接受整型数作为参数,也可以接受浮点数作为参数。
二. 虚函数的语法 三. 虚函数使用技巧
3.1 private的虚函数
附:C++中的虚函数和纯虚函数用法
1.虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract
class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。
2.虚函数可以被直接使用,也可以被子类(sub
class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base
只有声明而没有定义。
3.虚函数和纯虚函数都可以在子类(sub
class)中被重载,以多态的形式被调用。
4.虚函数和纯虚函数通常存在于抽象基类(abstract base class
-ABC)之中,被继承的子类重载,目的是提供一个统一的接口。
5.虚函数的定义形式:virtual {method body}
;纯虚函数的定义形式:virtual { } = 0;
在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time
bind),而且被两者修饰的函数生命周期(life recycle)也不一样。
6.如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。
以下为一个简单的虚函数和纯虚寒数的使用演示,目的是抛砖引玉!
//father class
class Virtualbase
virtual void Demon()= 0; //prue virtual
virtual void Base()
{cout&&"this is farther
//sub class
class SubVirtual :public
Virtualbase
void Demon() {
cout&&" this is
SubVirtual!"& void Base() {
cout&&"this
is subclass Base"&};
void main()
Virtualbase* inst = new SubVirtual();
//multstate pointer
inst-&Demon();
inst-&Base();
// inst = new Virtualbase();
// inst-&Base()
虚函数的标志是“virtual”关键字。
2.1 使用virtual关键字
考虑下面的类层次:
virtual void foo();
class B: public A
void foo(); // 没有virtual关键字!
class C: public B //
从B继承,不是从A继承!
void foo(); // 也没有virtual关键字!
这种情况下,B::foo()是虚函数,C::foo()也同样是虚函数。因此,可以说,基类声明的虚函数,在派生类中也是虚函数,即使不再使用virtual关键字。
2.2 纯虚函数
如下声明表示一个函数为纯虚函数:
virtual void foo()=0; //
=0标志一个虚函数为纯虚函数
一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。
2.3 虚析构函数
析构函数也可以是虚的,甚至是纯虚的。例如:
virtual ~A()=0; // 纯虚析构函数
当一个类打算被用作其它类的基类时,它的析构函数必须是虚的。考虑下面的例子:
A() { ptra_ = new char[10];}
~A() { delete[] ptra_;} //
非虚析构函数
char * ptra_;
class B: public A
B() { ptrb_ = new char[20];}
~B() { delete[] ptrb_;}
char * ptrb_;
void foo()
A * a = new B;
在这个例子中,程序也许不会象你想象的那样运行,在执行delete
a的时候,实际上只有A::~A()被调用了,而B类的析构函数并没有被调用!这是否有点儿可怕?
如果将上面A::~A()改为virtual,就可以保证B::~B()也在delete
a的时候被调用了。因此基类的析构函数都必须是virtual的。
纯虚的析构函数并没有什么作用,是虚的就够了。通常只有在希望将一个类变成抽象类(不能实例化的类),而这个类又没有合适的函数可以被纯虚化的时候,可以使用纯虚的析构函数来达到目的。
2.4 虚构造函数?
构造函数不能是虚的。
考虑下面的例子:
void foo() { bar();}
virtual void bar() { ...}
class B: public A
virtual void bar() { ...}
在这个例子中,虽然bar()在A类中是private的,但是仍然可以出现在派生类中,并仍然可以与public或者protected的虚函数一样产生多态的效果。并不会因为它是private的,就发生A::foo()不能访问B::bar()的情况,也不会发生B::bar()对A::bar()的override不起作用的情况。
这种写法的语意是:A告诉B,你最好override我的bar()函数,但是你不要管它如何使用,也不要自己调用这个函数。
3.2 构造函数和析构函数中的虚函数调用
一个类的虚函数在它自己的构造函数和析构函数中被调用的时候,它们就变成普通函数了,不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”。例如:
A() { foo();} //
在这里,无论如何都是A::foo()被调用!
~A() { foo();} // 同上
virtual void foo();
class B: public A
virtual void foo();
void bar()
A * a = new B;
如果你希望delete
a的时候,会导致B::foo()被调用,那么你就错了。同样,在new
B的时候,A的构造函数被调用,但是在A的构造函数中,被调用的是A::foo()而不是B::foo()。
3.3 多继承中的虚函数 3.4 什么时候使用虚函数
在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。
以设计模式[2]中Factory
Method模式为例,Creator的factoryMethod()就是虚函数,派生类override这个函数后,产生不同的Product类,被产生的Product类被基类的AnOperation()函数使用。基类的AnOperation()函数针对Product类进行操作,当然Product类一定也有多态(虚函数)。
另外一个例子就是集合操作,假设你有一个以A类为基类的类层次,又用了一个std::vector来保存这个类层次中不同类的实例指针,那么你一定希望在对这个集合中的类进行操作的时候,不要把每个指针再cast回到它原来的类型(派生类),而是希望对他们进行同样的操作。那么就应该将这个“一样的操作”声明为virtual。
现实中,远不只我举的这两个例子,但是大的原则都是我前面说到的“如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的”。这句话也可以反过来说:“如果你发现基类提供了虚函数,那么你最好override它”。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 虚函数作用 的文章

 

随机推荐