C++基类成员函数调用该类c 纯虚函数和虚基类,该怎么理解

C++中类的分层设计(纯虚函数) - 其他技术 - 电子工程世界网
C++中类的分层设计(纯虚函数)
11:35:59&&&来源:51hei &&
& &面向对象设计是一种思想,而C++恰好是一种面向对象的语言,在C++中设计类一般都会采用继承基类(父类),设计派生类(子类),继承和动态绑定的结合就巧妙的实现了实际的问题。但是如何设计出一个强大的类呢?今天在学习了纯虚函数以后,我又觉得分层设计的思想真的是一个非常棒的设计思想。
&&& 首先说明一下我只是一个初学者,写出来的东西可能会有错误,说实在的没写过多少代码,但是我认为写代码是一个熟练的过程,一个完成思想的过程。只有有了思想,才能完成好的设计,不断的思考,不断的总结才能学到新学到的东西,才能达到更高的目标。
&&& 在C++中虚函数是指将一个类中的虚函数后面加上=0,就说明该虚函数为纯虚函数。一般该函数设置为纯虚函数,那么该函数也就没有再次定义的必要了,因为存在纯虚函数的类就是一个抽象类,是不能够创建对象的,不能创建对象也就不会主动的调用该函数,哪怕在动态绑定的情况下也不会,因此不需要为该函数定义相关的操作,实质上在此处声明只是说明在该派生类中需要重写基类的虚函数,至于是否真的复写,不要去关心。
&&& //存在纯虚函数的类为抽象类
&&& class AbstructClass:public Base
&&& public:
&&&&&&& //构造函数
&&&&&&& AbstructClass(...):Base(...),...{}
&&&&&&& //析构函数
&&&&&&& ~AbstructClass(){...}
&&&&&& //这就是纯虚函数,一般func在基类中就为虚函数,因此virtual也可以不写
&&&&&&& virtual func(...) const = 0;
&&& //保证派生类能够访问数据成员,必须设置为protected
&&& protected:
&&&&&& //一些数据成员定义
&&&&&& ...
&& 抽象类的作用主要是帮助完成实际派生类的设计,为什么这么说呢?如果我们只创建一个基类,直接继承基类创建实际的派生类,存在很多的问题,为什么这么说呢?比如说我要创建一个人的类,我们每一个人都是一个人的对象,在人的基类中一般只是包含了人的共性,不可能将某一个人的特殊定义到类中,当然为了继承,肯定也会创建一系列的虚函数。如果我们每一个人都直接从该基类派生,人的个体在世界上有60多亿,我们每一个人实际上就是一个派生类,因为每一个人都不同,都有自己独特的特性(假设是一种数据对象),如果要实现这么多的派生类真的是一个不可想象的,而且我们可以知道很多人(类)实际上存在很多的相似性。
&&& 这时我们实际上就可以采用抽象类来帮忙完成我们的设计。抽象类是连接在基类和实际派生类之间的中间类,实际派生类的直接基类是抽象类,也就说说基类是实际派生类的间接基类。在抽象层中主要完成什么操作呢?抽象层中主要完成对派生类共性数据成员的定义,为了方便派生类的数据访问,必须设置为受保护访问权限,创建纯虚函数,定义构造函数,应该在初始化列表中首先创建基类对象,然后才能完成其他成员的初始化,有时候可能需要复制控制函数的实现。
&&& 在实际的派生类设计过程中就不再直接从基类继承,而是从抽象类中继承,因为抽象类中增加了一些受保护成员数据,且这些数据成员时派生类的共性,因此派生类中可以很方便的访问。同时在该派生类中就应该完成虚函数的复写操作,因为动态绑定以后会直接调用该版本的虚函数。当然有时候也要完成复制控制函数的定义。当然可以设计很多基于抽象类的派生类。当然在派生类中也可以增加自己的数据成员。
&&& //实际派生类从抽象类中继承
&&& class ActualClass : public AbstructClass
&&& public:
&&&&&&& ActualClass(...):AbstructClass(...),...{}
&&&&&&& //派生类中复写虚函数
&&&&&&& func()
&&&&&&& {...}
&&& private:
&&&&&&& //派生类的一些数据
&&&&&&& ...
基本的思想如下图所示:
&& 从上面的图可知,我们可以在实际的派生类与基类之间增加一个中间层,这种实现方式不仅能够更好的隐藏数据,而且比较好的解决了我们上面提到的派生类过实现过于复杂的问题。因此我们可以认为抽象类实际上就是一个分层设计的方法,也可以认为是一个分成更加精细子类的方式方法,也就是说在基类的基础上将对象分成很多子类(抽象类),然后在各个子类下设计新的派生类。比如在图中的抽象类1,抽象类2是不同的,是两种不同的分类,这候我们给予两个抽象类的派生类当然也就存在了差别。这样实现的好处能够避免很多的重复代码。
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。
C++相关资源
分层设计相关资源
纯虚函数相关资源
编辑:什么鱼
本文引用地址:
大学堂最新课程
本周热门资源推荐
EEWORLD独家&抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。
&&&&& ⑴抽象类的定义:
&&&&& 称带有纯虚函数的类为抽象类。
&&&&& ⑵抽象类的作用:
&&&&& 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。
&&&&& (3)使用抽象类时注意:
&&& 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。
&&& 抽象类是不能定义对象的。
 纯是一种特殊的虚函数,它的一般格式如下:
  class &类名&
  virtual &类型&&函数名&(&参数表&)=0;
  在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的去做。这就是纯虚函数的作用。
  纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。除非在派生类中完全实现基类中所有的的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
  2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔 雀等子类,但动物本身生成对象明显不合常理。
  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;)。若要使派生类为非抽象类,则要求在派生类中,必须对纯虚函数予以重载以实现。同时含有纯虚函数的类称为,它不能生成对象。这样就很好地解决了上述两个问题。
&&&&&&&&&例如,绘画程序中,shape作为一个基类可以派生出圆形、矩形、正方形、梯形等, 如果我要求面积总和的话,那么会可以使用一个 shape * 的数组,只要依次调用派生类的area()函数了。如果不用接口就没法定义成数组,因为既可以是circle ,也可以是square ,而且以后可能加上rectangle,等等.
&&&&& 1、多态性
  指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
  a.编译时多态性:通过重载函数实现
  b 运行时多态性:通过虚函数实现。
  2、虚函数
  虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载
  3、抽象类
  包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。
  重载是一种是多态(如C++),有四种形式的多态:
  1.虚多态
  2模板多态
  所谓的动态和静态区分是另一种基于绑定时间的多态分类,严格来说,重载是编译时多态,即静态多态,根据不同类型函数编译时会产生不同的名字如int_foo和char_foo等等,以此来区别调用。故重载仍符合多态定义&&通过单一标识支持不同特定行为的能力,只是重载属于静态多态,而不是通过继承和虚函数实现的动态多态。
  重载(overload)和多态无关,真正和多态相关的是覆盖(override)。
  当重新定义了基类的虚拟方法后,基类根据赋给它的不同的派生类引用,动态地调用属于派生类的对应方法,这样的方法调用在编译期间是无法确定的。因此,这样的方法地址是在运行期绑定的(动态绑定)。
  重载只是一种语言特性,是一种语法规则,与多态无关,与也无关。
  不过针对所谓的第二种重载,有一个专门的名词--重写或重定义。重载与重写的区别就在于是否覆盖,重写一般多发生在不同的类且存在继承关系之间,而重载多是在一个类里或者一块代码段里。
  函数重载:
  函数重载就是函数名称相同,但定义却不同。函数重载使得程序员可以将一系列的函数族定义为一个统一的界面,但是却可以处理不同类型数据或接受不同个数的参数。这实现了统一接口,不同定义的思想。
  流的重载:
  C++的流提取运算符&&和就插入运算符&&是C++类库提供的输入输出(I/O)操作符。系统与定义了输入流类istream和输出流类ostream,cin和cout分别是istream和ostream的对象。iostream头文件中已经对&&和&&进行了重载,使得它能用来输入/输出标准类型的数据。但如果是自己定义类型,就需要重载在这两个运算符,以便它们能输入/上岁数出该自定义类型。格式如下:
  流插入的重载:ostream &operator&&(ostream &,自定义类 &);
  流提取的重载:istream &operator&&(istream &,自定义类 &);
  运算符的重载:
  运算符的重在就是对运算符的重新定义,即一个运算符可以用于多种数据类型的运算中。运算符的重载在实际编程中使用很普遍,例如,&+&运算符既可以用于整数加,也可以用于浮点加,甚至是字符串的链接。格式如下:
  type operator sign (参数列表);
阅读(...) 评论() &在说明其作用前先看一段代码
class B:public A
void bPrintf(){cout&&&This is class B&&&};
class C:public A
void cPrintf(){cout&&&This is class C&&&};
class D:public B,public C
void dPrintf(){cout&&&This is class D&&&};
void main()
cout&&d.iValue&& //错误,不明确的访问
cout&&d.A::iValue&& //正确
cout&&d.B::iValue&& //正确
cout&&d.C::iValue&& //正确
从代码中可以看出类B C都继承了类A的iValue成员,因此类B C都有一个成员变量iValue ,而类D又继承了B C,这样类D就有一个重名的成员 iValue(一个是从类B中继承过来的,一个是从类C中继承过来的).在主函数中调用d.iValue
因为类D有一个重名的成员iValue编译器不知道调用 从谁继承过来的iValue所以就产生的二义性的问题.正确的做法应该是加上作用域限定符
d.B::iValue 表示调用从B类继承过来的iValue。不过 类D的实例中就有多个iValue的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。
class B:virtual public A
void bPrintf(){cout&&&This is class B&&&};
class C:virtual public A
void cPrintf(){cout&&&This is class C&&&};
class D:public B,public C
void dPrintf(){cout&&&This is class D&&&};
void main()
cout&&d.iValue&& //正确
在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如iValue这个成员,从类
D这个角度上来看,它是从类B与类C继承过来的,而类B C又是从类A继承过来的,但它们只保留一个副本。因此在主函数中调用d.iValue时就不
会产生错误。&
还是先看代码
void funPrint(){cout&&&funPrint of class A&&&};
class B:public A
void funPrint(){cout&&&funPrint of class B&&&};
void main()
A *p; //定义基类的指针
p-&funPrint();
p-&funPrint();
大家以为这段代码的输出结果是什么?有的人可能会马上回答funPrint of class A 与 funPrint of class B 因为第一次输出是引用类A的实 例啊,第二次输出是引用类B的实例啊。那么我告诉你这样想就错啦,答案是funPrint
of class A 与 funPrint of class A 至于为什么输出 这样的结果不在本文讨论的范围之内;你就记住,不管引用的实例是哪个类的当你调用的时候系统会调用左值那个对象所属类的方法。比如说
上面的代码类A B都有一个funPrint 函数,因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了。可是如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到
虚函数(也就是动态联篇)
virtual void funPrint(){cout&&&funPrint of class A&&&};
class B:public A
virtual void funPrint(){cout&&&funPrint of class B&&&};
void main()
A *p; //定义基类的指针
p-&funPrint();
p-&funPrint();
在基类的成员函数前加virtual关键字表示这个函数是一个虚函数,所谓虚函数就是在编译的时候不确定要调用哪个函数,而是动态决定将要调
用哪个函数,要实现虚函数必须派生类的函数名与基类相同,参数名参数类型等也要与基类相同。但派生类中的virtual关键字可以省略,也表 示这是一个虚函数。下面来解决一下代码,声明一个基类的指针(必须是基类,反之则不行)p,把p指向类A的实例a,调用funPrint函数,这
时系统会判断p所指向的实例的类型,如果是A类的实例就调用A类的funPrint函数,如果是B类的实例就调用B类的funPrint函数。
与其叫纯虚函数还不如叫抽象类,它只是声明一个函数但不实现它,让派生类去实现它,其实这也很好理解。&
class Vehicle
virtual void PrintTyre()=0;
//纯虚函数是这样定义的
class Camion:public Vehicle
virtual void PrintTyre(){cout&&&Camion tyre four&&&};
class Bike:public Vehicle
virtual void PrintTyre(){cout&&&Bike tyre two&&&};
void main()
b.PrintTyre();
c.PrintTyre();
如上代码,定义了一个交通工具类(Vehicle),类中有一函数可打印出交通工具的轮胎个数,但交通工具很多轮胎个数自然也就不确定,所以 就把它定义为纯虚函数,也就是光定义函数名不去实现它,类Camion继承了Vehicle并实现了里面的代码,打印出有4个轮胎。Bike类也是一样。
有一点须要注意一下,纯虚函数不能实化化,但可以声明指针。
1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。&
2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。&
3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。
4, 最派生类是指在继承结构中建立对象时所指定的类。&
5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。&
6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象
只初始化一次。&
7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。&
1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。&
2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。&
3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。&
4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。&
5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。
纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。
1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。&
2, 纯虚函数的作用是为派生类提供一个一致的接口。&
3, 纯虚函数不能实化化,但可以声明指针。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:98840次
积分:2349
积分:2349
排名:第16866名
原创:139篇
转载:26篇
(4)(12)(4)(7)(8)(9)(1)(7)(7)(4)(3)(7)(19)(11)(5)(8)(1)(1)(3)(10)(10)(12)(9)(3)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'&re: 纯虚函数能为private吗?&&&&
大牛!谢谢
阅读排行榜
评论排行榜posts - 214,&
comments - 20,&
trackbacks - 0
转自:CSDN
★抽象类:一个类可以抽象出不同的对象来表达一个抽象的概念和通用的接口,这个类不能实例化(创造)对象。
★纯虚函数(pure virtual):在本类里不能有实现(描述功能),实现需要在子类中实现。例:&virtual typeT function_name(parameter_list)=0;&&virtual void draw()=0;&//画,纯虚函数;&virtual void rotate(double)=0;&//旋转,纯虚函数;
★抽象类(abstract class):如果一个类包含纯虚函数,那么这个类就叫抽象类。
★一个抽象类只能用作基类,只能用作派生,不能实例化(创建)对象。一个类要是包含至少一个纯虚函数,则这个类是抽象类。一个抽象类的子类可以添加更多的数据成员和成员函数。
★抽象类的子类可以还是抽象类,可以添加更多的成员函数和成员方法,直到可以产生对象为止。
★由于抽象类不能构造对象,因此它的构造函数不能被单独调用。它的构造函数只能在子类的成员初始化列表里面调用。
★抽象类不一定有析构函数,如果有必须是虚析构函数。
★★★一个函数不能有抽象类对象的值参数&参数不能传值&,这个函数不能有抽象类对象的值返回。然而可以有抽象类类型的指针和引用可以作为参数,同样抽象类的指针和引用可以作为函数的返回值类型。因为他们可以指向或者引用抽象类的子类对象。
★纯虚函数是在子类里面被实现的。如果子类没有实现纯虚函数,纯虚函数将继承给子类。那么这时子类同样也是一个抽象类。
阅读(...) 评论() &

我要回帖

更多关于 纯虚函数的调用 的文章

 

随机推荐