虚基类 、 纯虚函数和抽象类

news/2024/7/4 1:31:26 标签: class, c, delete
cle class="tags" href="/tags/CLASS.html" title=class>class="baidu_pl">
cle_content" class="tags" href="/tags/CLASS.html" title=class>class="article_content clearfix">
content_views" class="tags" href="/tags/CLASS.html" title=class>class="htmledit_views"> class="tags" href="/tags/CLASS.html" title=class>class="Apple-style-span" style="font-family: 宋体;">  class="tags" href="/tags/CLASS.html" title=class>class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: ����; ">class="tags" href="/tags/CLASS.html" title=class>class="Apple-style-span" style="font-family: 宋体;">虚基类   
    
          在《多继承》中讲过的例子中࿰c;由类A࿰c;类B1和类B2以及类C组成了类继承的层次结构。在该结构中࿰c;类C的对象将包含两个类A的子对象。由于类A是派生类C两条继承路径上的一个公共基类࿰c;那么这个公共基类将在派生类的对象中产生多个基类子对象。如果要想使这个公共基类在派生类中只产生一个基类子对象࿰c;则必须将这个基类设定为虚基类。   
    
          虚基类的引入和说明   
    
          前面简单地介绍了要引进虚基类的原因。实际上࿰c;引进虚基类的真正目的是为了解决二义性问题。   
    
  虚基类说明格式如下:   
    
          virtual   <继承方式><基类名>   
    
  其中࿰c;virtual是虚类的关键字。虚基类的说明是用在定义派生类时࿰c;写在派生类名的后面。例如:   
    
  class="tags" href="/tags/CLASS.html" title=class>class   A   
  {   
  public:   
          void   f();   
  protected:   
          int   a;   
  };   
  class="tags" href="/tags/CLASS.html" title=class>class   B   :   virtual   public   A   
  {   
  protected:   
          int   b;   
  };   
  class="tags" href="/tags/CLASS.html" title=class>class   C   :   virtual   public   A   
  {   
  protected:   
          int   c:   
  };   
  class="tags" href="/tags/CLASS.html" title=class>class   D   :   public   B,   public   C   
  {   
  public:   
          int   g();   
  private:   
          int   d;   
  };   
    
  由于使用了虚基类࿰c;使得类A࿰c;类B࿰c;类C和类D之间关系用DAG图示法表示如下:   
    
                                  A{   f(),   a   }   
                                      /           /   
                                    B{b}     C{c}   
                                      /           /   
                                    D{g(),d}   
    
          从该图中可见不同继承路径的虚基类子对象被合并成为一个对象。这便是虚基类的作用࿰c;这样将消除了合并之前可能出现的二义性。这时࿰c;在类D的对象中只存在一个类A的对象。因此࿰c;下面的引用都是正确的:   
    
  D   n;   
  n.f();   //对f()引用是正确的。   
  void   D::g()   
  {   
          f();   //对f()引用是正确的。   
  }   
    
  下面程序段是正确的。   
    
  D   n;   
  A   *pa;   
  pa   =   &n;   
    
          其中࿰c;pa是指向类A对象的指针࿰c;n是类D的一个对象࿰c;&n是n对象的地址。pa=&n是让pa指针指向类D的对象࿰c;这是正确的࿰c;并且也无二义性。   
    
  虚基类的构造函数   
    
          前面讲过࿰c;为了初始化基类的子对象࿰c;派生类的构造函数要调用基类的构造函数。对于虚基类来讲࿰c;由于派生类的对象中只有一个虚基类子对象。为保证虚基类子对象只被初始化一次࿰c;这个虚基类构造函数必须只被调用一次。由于继承结构的层次可能很深࿰c;规定将在建立对象时所指定的类称为最派生类。C++规定࿰c;虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。如果一个派生类有一个直接或间接的虚基类࿰c;那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用。如果未被列出࿰c;则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象。   
    
          从虚基类直接或间接继承的派生类中的构造函数的成员初始化列表中都要列出这个虚基类构造函数   的调用。但是࿰c;只有用于建立对象的那个最派生类的构造函数调用虚基类的构造函数࿰c;而该派生类的基类中所列出的对这个虚基类的构造函数调用在执行中被忽略࿰c;这样便保证了对虚基类的对象只初始化一次。   
    
          C++又规定࿰c;在一个成员初始化列表中出现对虚基类和非虚基类构造函数的调用࿰c;则虚基类的构造函数先于非虚基类的构造函数的执行。   
    
  下面举一例子说明具有虚基类的派生类的构造函数的用法。   
    
  #include   <iostream.h>   
  class="tags" href="/tags/CLASS.html" title=class>class   A   
  {   
  public:   
          A(const   char   *s)   {   cout<<s<<endl;   }   
          ~A()   {}   
  };   
    
  class="tags" href="/tags/CLASS.html" title=class>class   B   :   virtual   public   A   
  {   
  public:   
          B(const   char   *s1,   const   char   *s2):A(s1)   
          {   
                  cout<<s2<<endl;   
          }   
  };   
    
  class="tags" href="/tags/CLASS.html" title=class>class   C   :   virtual   public   A   
  {   
  public:   
          C(const   char   *s1,   const   char   *s2):A(s1)   
          {   
          cout<<s2<<endl;   
          }   
  };   
    
  class="tags" href="/tags/CLASS.html" title=class>class   D   :   public   B,   public   C   
  {   
  public:   
          D(const   char   *s1,   const   char   *s2,   const   char   *s3,   const   char   *s4)   
                  :B(s1,   s2),   C(s1,   s3),   A(s1)   
          {   
                  cout<<s4<<endl;   
          }   
  };   
    
  void   main()   
  {   
          D   *ptr   =   new   D("class="tags" href="/tags/CLASS.html" title=class>class   A",   "class="tags" href="/tags/CLASS.html" title=class>class   B",   "class="tags" href="/tags/CLASS.html" title=class>class   C",   "class="tags" href="/tags/CLASS.html" title=class>class   D");   
          delete   ptr;   
  }   
    
  该程序的输出结果为:   
    
          class="tags" href="/tags/CLASS.html" title=class>class   A   
          class="tags" href="/tags/CLASS.html" title=class>class   B   
          class="tags" href="/tags/CLASS.html" title=class>class   C   
          class="tags" href="/tags/CLASS.html" title=class>class   D   
    
          在派生类B和C中使用了虚基类࿰c;使得建立的D类对象只有一个虚基类子对象。   
    
          在派生类B࿰c;C࿰c;D的构造函数的成员初始化列表中都包含了对虚基类A的构造函数。   
    
          在建立类D对象时࿰c;只有类D的构造函数的成员初始化列表中列出的虚基类构造函数被调用࿰c;并且仅调用一次࿰c;而类D基类的构造函数的成员初始化列表中列出的虚基类构造函数不被执行。这一点将从该程序的输出结果可以看出。   
    
  2001-8-21   23:57                       
    
  上一讲   |   返回   |   下一讲   
  --------------------------------------------------------------------------------   
    
    
  纯虚函数和抽象类   
    
    
          纯虚函数是一种特殊的虚函数࿰c;它的一般格式如下:   
    
  class="tags" href="/tags/CLASS.html" title=class>class   <类名>   
  {   
          virtual   <类型><函数名>(<参数表>)=0;   
          …   
  };   
    
          在许多情况下࿰c;在基类中不能对虚函数给出有意义有实现࿰c;而把它说明为纯虚函数࿰c;它的实现留给该基类的派生类去做。这就是纯虚函数的作用。下面给出一个纯虚函数的例子。   
    
  #include   <iostream.h>   
    
  class="tags" href="/tags/CLASS.html" title=class>class   point   
  {   
  public:   
          point(int   i=0,   int   j=0)   {   x0=i;   y0=j;   }   
          virtual   void   set()   =   0;   
          virtual   void   draw()   =   0;   
  protected:   
          int   x0,   y0;   
  };   
    
  class="tags" href="/tags/CLASS.html" title=class>class   line   :   public   point   
  {   
  public:   
          line(int   i=0,   int   j=0,   int   m=0,   int   n=0):point(i,   j)   
          {   
          x1=m;   y1=n;   
          }   
          void   set()   {   cout<<"line::set()   called./n";   }   
          void   draw()   {   cout<<"line::draw()   called./n";   }   
  protected:   
          int   x1,   y1;   
  };   
    
  class="tags" href="/tags/CLASS.html" title=class>class   ellipse   :   public   point   
  {   
  public:   
          ellipse(int   i=0,   int   j=0,   int   p=0,   int   q=0):point(i,   j)   
          {   
          x2=p;   y2=q;   
          }   
          void   set()   {   cout<<"ellipse::set()   called./n";   }   
          void   draw()   {   cout<<"ellipse::draw()   called./n";   }   
  protected:   
          int   x2,   y2;   
  };   
    
  void   drawobj(point   *p)   
  {   
          p->draw();   
  }   
    
  void   setobj(point   *p)   
  {   
          p->set();   
  }   
    
  void   main()   
  {   
          line   *lineobj   =   new   line;   
          ellipse   *elliobj   =   new   ellipse;   
          drawobj(lineobj);   
          drawobj(elliobj);   
          cout<<endl;   
          setobj(lineobj);   
          setobj(elliobj);   
          cout<<"/nRedraw   the   object.../n";   
          drawobj(lineobj);   
          drawobj(elliobj);   
  }   
    
  抽象类   
    
          带有纯虚函数的类称为抽象类。抽象类是一种特殊的类࿰c;它是为了抽象和设计的目的而建立的࿰c;它处于继承层次结构的较上层。抽象类是不能定义对象的࿰c;在实际中为了强调一个类是抽象类࿰c;可将该类的构造函数说明为保护的访问控制权限。   
    
          抽象类的主要作用是将有关的组织在一个继承层次结构中࿰c;由它来为它们提供一个公共的根࿰c;相关的子类是从这个根派生出来的。   
    
          抽象类刻画了一组子类的操作接口的通用语义࿰c;这些语义也传给子类。一般而言࿰c;抽象类只描述这组子类共同的操作接口࿰c;而完整的实现留给子类。   
    
          抽象类只能作为基类来使用࿰c;其纯虚函数的实现由派生类给出。如果派生类没有重新定义纯虚函数࿰c;而派生类只是继承基类的纯虚函数࿰c;则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现࿰c;则该派生类就不再是抽象类了࿰c;它是一个可以建立对象的具体类了。   
    
  2001-9-14   16:34                       
    
  上一讲   |   返回   |   下一讲   
    
  --------------------------------------------------------------------------------   
                            ----摘自《C++面向对象程序设计基础教程》   

cle>

http://www.niftyadmin.cn/n/1139195.html

相关文章

2011计算机等级考试二级c语言公共基础教程.doc,2011年-我国计算机的等级考试二级c语言公共基础知识复习100题及答案.doc...

...PAGE...2011年全国计算机等级考试二级公共基础知识复习100题1.1 数据结构作为计算机的一门学科&#xff0c;主要研究数据的逻辑结构、对各种数据结构进行的运算&#xff0c;以及 A)数据的存储结构? B)计算方法? C)数据映象? D)逻辑存储 正确答案: A1.2 数据处理的最小单位…

46家中外知名企业面试题目

微软  智力题  1&#xff0e;烧一根不均匀的绳子&#xff0c;从头烧到尾总共需要1个小时&#xff0c;问如何用烧绳子的方法来确定半小时的时间呢&#xff1f;  2&#xff0e;10个海盗抢到了100颗宝石&#xff0c;每一颗都一样大小且价值连城。他们决定这么分&#xff1a;…

syntaxerror是什么错误_一行代码简化Python异常信息:错误清晰指出,排版简洁美观 | 开源...

鱼羊 发自 凹非寺量子位 报道 | 公众号 QbitAI即使是Python&#xff0c;报错时也令人头大。看着这一堆乱麻&#xff0c;不知道是该怀疑人生&#xff0c;还是怀疑自己手残。那么&#xff0c;Python异常输出美化工具PrettyErrors了解一下&#xff1f;只需一个import&#xff0c;报…

计算机中任务管理器的主要功能是什么,任务管理器的作用有哪些 可以解决9成电脑问题...

大家一定都用过电脑任务管理器这项功能&#xff0c;一般来说都是用来强制关闭一些软件什么的&#xff0c;但其实&#xff0c;任务管理器的作用比你想象的还要多。01.DIY系统监视器&#xff0c;时刻监测电脑状态对于任务管理器&#xff0c;S姐我一直有个形象的比喻&#xff0c;它…

C++内存对象

一&#xff0e;基本概念   先来看看栈。栈&#xff0c;一般用于存放局部变量或对象&#xff0c;如我们在函数定义中用类似下面语句声明的对象&#xff1a;Type stack_object ;  stack_object便是一个栈对象&#xff0c;它的生命期是从定义点开始&#xff0c;当所在函数返回…

计算机系统操作工中级工试卷,计算机系统操作工中级理论知识试卷7 份

职业技能鉴定国家题库 计算机系统操作工中级理论知识试卷注 意 事 项 线1、本试卷依据2001年颁布的《计算机系统操作工》国家职业标准命制&#xff0c; 考试时间&#xff1a;120分钟。 2、请在试卷标封处填写姓名、准考证号和所在单位的名称。 此3、请仔细阅读答题要求&#xf…

中信银行计算机岗位面试自我介绍,请问中信银行面试如何写自我介绍?

一、第一句话(first word)见到考官的第一句话&#xff0c;很关键&#xff0c;不用说的很复杂。可以是一个简单句&#xff0c;但一定要铿锵有力。展示出自信和实力。千万不要来一句“sorry&#xff0c; my English is poor”。常见的开头有&#xff1a;1. Good morning! may I i…

python re模块_Python 中的 re 模块

封面图片来源&#xff1a;沙沙野re 模块查找某个元素import res "好好学习&#xff0c;天天向上好好" print(s.find("天天")) # 5 print(re.findall("好好", s)) # [好好, 好好]2.『 w 』与『 W』w&#xff1a;匹配字母中文数字下划线…