您的位置:首页 > 编程语言 > C语言/C++

C++中的虚函数和纯虚函数用法

2013-10-10 17:52 162 查看
使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。 如果父类的函数(方法)根本没有必要或者无法实现,完全要依赖子类去实现的话,可以把此函数(方法)设为virtual 函数名=0 我们把这样的函数(方法)称为纯虚函数。
也就是说,虚函数 ,子类可以不重写,直接继承父类 的方法来使用,也可以重写
但是 纯虚函数是子类是必须重写了才可以使用


虚函数是为了实现多态性:允许使用统一的接口去访问不同子类的函数。

[cpp] view
plaincopyprint?

class A

{

public:

A(){}

virtual void print()

{

cout<<"I am A"<<endl;

}

};

class B:public A

{

public:

B(){};

void print()

{

cout<<"I am B"<<endl;

}

};

class C:public A

{

public:

C(){};

void print()

{

cout<<"I am C"<<endl;

}

};

int main()

{

A *a =new B();

(*a).print();// I am B

A* a1=new C();

(*a).print();// I am C

//同样的接口确输出不同的结果,这就是多态性。

return 0;

}

虚继承是为了解决多重继承而出现的,能够解决菱形继承中二义性和节省内存开销



代码如下:

class A;

class B: public virtual A;

class C: public virtual A;

class D: public B,public C;

如果class B和class C不采用虚继承,那么在D中将会有两份A的拷贝,在调用D中的从A中间接继承下来的函数时会出现二义性,并且内存开销较大。

定义一个函数为纯虚函数,代表函数没有被实现。定义他是为了实现一个接口,起到一个规范的作用,规范继承这个。类的程序员必须实现这个函数。

纯虚函数定义如下: virtual void D()=0;

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。

2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)

只有声明而没有定义。

3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。

4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。

5. 虚函数的定义形式:virtual {method body}

纯虚函数的定义形式:virtual { } = 0;

在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。

6. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。

以下为一个简单的虚函数和纯虚寒数的使用演示,目的是抛砖引玉!

#include

//father class

class Virtualbase

{

public:

virtual void Demon()= 0; //纯虚函数

virtual void Base() {cout<<"this is farther class"<};

class SubVirtual :public Virtualbase //子类

{

public:

void Demon() { cout<<" this is SubVirtual!"< }

void Base() { cout<<"this is subclass Base"};

/* instance class and sample */

void main()

{

Virtualbase* inst = new SubVirtual(); //multstate pointer

inst->Demon();

inst->Base();

// inst = new Virtualbase();

// inst->Base()

return ;

}

************************************************************************************************************************************************8

abstract class是抽象类,至少包含一个纯虚函数的类就叫做抽象类。

但是如果一个类,所有的成员都是纯虚函数,那么它和一般的抽象类在用法上是有区别的。至少Microsoft给的COM接口定义全部都是仅由纯虚函数构成的类。因此把这样的类定义叫做纯虚类也不算错。

纯虚函数和虚函数的区别在于前者不包含定义,而后者包含函数体。

那么纯虚类就是不包含任何实现(包括成员函数定义和成员变量定义。前者代表算法,后者代表结构)。不包含任何算法和结构的类叫做纯虚类,应该没有问题。

justforfun626说我confused with the name of abstract class,那是因为在Java里面的确没有纯虚类的概念,因为Java里没有纯虚函数这个概念。Java管虚函数叫做abstract function,管抽象类叫做abstract class,直接说来,Java根本没有virtual这个关键字,都用abstract代替,因此Java里面根本就没有Pure这个概念。有那就是interface。在interface里面定义的函数都不能有函数体,这个在Java里面叫做接口。那么C++里面与interface等同的概念就是纯虚类了,C++用纯虚类来模拟interface这个抽象概念,因此这里说的“纯虚类”与Java的abstract
class不同,与C++的一般抽象类也不同。“纯虚类”与C++一般抽象类的区别就好比Java里面interface 和 abstract class的区别。

----------------------------------

//今天深刻理解了虚函数和纯虚函数的作用和用法

//如果子类实现了虚函数则调用子类的方法,

//如果子类没有实现该函数则调用父类的方法。

//究竟怎么用呢:

假如CChild 派生自 CMan ,有个虚函数 Eat();

CMan m_man;

CChild m_child;

//这才是使用的精髓,如果不定义基类的指针去使用,没有太大的意义

CMan *p ;

p = &m_man ;

p->Eat(); //始终调用CMan的Eat成员函数,不会调用 CChild 的

p = &m_child;

p->Eat(); //如果子类实现了该方法,则始终调用CChild的Eat函数

//不会调用CMan 的 Eat 方法 如果子类没有实现该函数

//则调用CMan的Eat函数

//呵呵,就这些了,还得用心体会

//纯虚函数就是基类只定义了函数体,没有实现过程 定义方法如下

// virtual void Eat() = 0; 直接=0 不要 在cpp中定义就可以了

//纯虚函数相当于接口,不能直接实例话,需要派生类来实现函数定义

//有的人可能在想,定义这些有什么用啊 ,我觉得很有用

//比如 你想描述一些事物的属性给别人,而自己不想去实现,就可以定

//义为纯虚函数。说的再透彻一些。比如盖楼房,你是老板,你给建筑公司

//描述清楚你的楼房的特性,多少层,楼顶要有个花园什么的

//建筑公司就可以按照你的方法去实现了,如果你不说清楚这些,可能建筑

//公司不太了解你需要楼房的特性。用纯需函数就可以很好的分工合作了

//你觉得有道理吗?

---------------------------------

2、抽象类:

抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。

⑴抽象类的定义:

称带有纯虚函数的类为抽象类。

⑵抽象类的作用:

抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。

(3)使用抽象类时注意:

? 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。

? 抽象类是不能定义对象的。

---------------------------------------------

(原名)我对C++中虚函数、纯虚函数在实现多态中作用的一点浅薄认识

mahongxi(原作)

多态是面向对象程序设计和面向过程程序设计的主要区别之一,何谓多态?记得在CSDN里一篇论C++多态的文章里有一名话:“龙生九子,子子不同”多态就是同一个处理手段可以用来处理多种不同的情况,在钱能老师的《C++程序设计教程》书中有这样一个例子:

定义了一个小学生类

[本文全部代码均用伪码]

class Student

{public:

Student(){}

~Student(){}

void 交学费(){}

//......

};

里面有一个 “交学费”的处理函数,因为大学生和小学生一些情况类似,我们从小学生类中派生出大学生类:

class AcadStudent:public Student

{public:

AcadStudent(){}

~ AcadStudent(){}

void 交学费(){}

//.......

};

我们知道,中学生交费和大学生交费情况是不同的,所以虽然我们在大学生中继承了中学生的"交学费"操作,但我们不用,把它重载,定义大学生自己的交学费操作,这样当我们定义了一个小学生,一个大学生后:

Student A;

AcadStudent B;

A.交学费(); 即调用小学生的,B.交学费();是调用大学生的,功能是实现了,但是你要意识到,可能情况不仅这两种,可能N种如:小学生、初中生、高中生、研究生.....

它们都可以以Student[小学生类]为基类。

如果系统要求你在一群这样的学生中,随便抽出一位交纳学费,你怎么做?

:

//A为抽出来的要交学费的同学

{switch(typeof(A))

{case 小学生:A.小学生:: 交学费 ();break;

case 初中生:A.初学生:: 交学费 ();break;

case 高中生:A.高学生:: 交学费 ();break;

default:

.............

}

}

首先,我们要在每个类中定义一个 typeof()用来识别它的类型,然后还要在程序中进行区别,这样一来,虽然也行,但是,如果再增加类型则要改动switch,又走了面向过程的老路,而且想通过一个模块进行操作实现起来也有难度。

所以C++中提供了多态,即能通过迟后联编的技术实现动态的区分。

在基类的"交学费"前加个Virtual 用来告诉系统,遇到这个处理过程要等到执行时再确定到底调用哪个类的处理过程。这样一来就可以:

void 通用的交学费操作 (Student &A)

{A.交学费();

}

一下全部搞定,你再加新的类型我也不怕!!![具体的实现原理参考:《Inside The C++ Object Model》]。如果没有 virtual这一声明,那么,系统在执行前就确定了操作,比如把“大学生”传给

void 通用的交学费操作 (Student &A)

{A.交学费();

}

,则A.交学费();调用的是大学生类中继承于Student类中的“交学费操作”

所以虚函数对多态的实现是必要的。

为什么会出现纯虚函数呢?

如果按上面的例子进行编程,所有类型都继承小学生类,我们会发现一此小学生自己特定的东东[比如 void 上美术课();],也都被大学生继承来了,虽然不影响大学生的操作,但是随时间的加长,小学生类中自已所特定的东东越来越多,这样下去,大学生中冗余的数据就多了,有什么办法可以解决????

就是定义基类时定义一个抽象类,如学生类,在学生类中实现一此大家都有的操作

。这个过程就叫分解。

这个类子对纯虚函数的说明还不够明显,换个例子比如:

class 人()

{public :

//......

void 吃()

{人吃饭;

}

//......

char *Name;

int age;

};

class 狗()

{public :

//......

void 吃()

{狗吃屎;

}

//......

char *Name;

int age;

};

人类、狗类有一些相同的东东,如名字,年纪,吃的动作等,有人想到了为了代码的重用,让人类继承狗类,可是数据的冗余让这个想法完蛋了,所以有人又想出可不可以定义一个这样的类:

这个类界于人类狗类之间,有人类和狗类共有的一些东东,比如年纪,名字,体重什么的,但它是不存在实例的,它的存在意义也是只是为了派生其它类,如人类、狗类,这样可以使系统清淅、。。。。。、、反正好处多多。

在这个世界上根本不存在界于人狗之间的东东,所以这个“人狗之间类”中的“吃”也是没什么意义,你也很难为它的内容下个定义,况且也没必要,所以定义它为纯虚函数,形式为:virtual void 吃()=0; 用来告诉系统:

1、这个函数可以没有函数定义;

2、拥有本函数的类是抽象类;

你可能会说,即然纯虚函数没什么意义,为什么还要它,它的作用是什么呢?

为实现多态作准备!!!

由于抽象类的实例没意义,所以C++中不允许定义它的实例。(如果定义了这样的实例A,那么你调用A.吃()怎么办?)

当然了,你也可以在基类中,virtual 吃(){}

这样一来,基类就不是抽象类了,可以有实例,而且对于其它方面都不影响。

但你也要承认这样的对象是没有什么意识的,它的存在只能使你思考上增加负担,除错时还要考虑到是不是有这样类的对象在作怪,所以C++干脆提供了“虚函数”、抽象类,的机制,给我们操作时加了限制也可以认为是保险[不可以有抽象类的对象],试想当代码变得非常之庞大时,它的存在是多么有必要啊!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: