接口和抽象类的区别

个人资料
天天
天天
  • 博客等级:
  • 博客积分:126
  • 博客访问:304
  • 关注人气:0
谁看过这篇博文

shenzhan87

中天数字

3月23日

sunny_nuix

flower…

3月11日

百分百纯咖啡

漠北

2月22日

正文 字体大小:  

[转载]C++中的抽象类以及接口的区别联系

  (2012-02-16 12:38:49)
标签: 

转载

分类: 烦死也要学技术=。=

篇文章转载C++之父关于接口在实现中问题的回答后,自己动手写了写。但是由于之前用C#写过一些程序,对abstract关键字理解不够深,结果深受其害,查阅了很多关于虚函数、纯虚函数、抽象类以及接口的实现方面的资料,现将它们之间的联系区别整理一下。

一 抽象类和接口

抽象类 

抽象类是特殊的类,不能被实例化(将定义了纯虚函数的类称为抽象类);除此以外,具有类的其他特性;

重要的是抽象类可以包括抽象方法,这是普通类所不能的,但同时也能包括普通的方法

抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们

另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们。

虽然不能定义抽象类的实例,但是可以定义它的指针,这正是用抽象类实现接口的重点所在。

接口 接口是一个概念。它在C++中用抽象类来实现,在C#和Java中用interface来实现。

 接口是引用类型的,类似于类,和抽象类的相似之处有三点:

        1、不能实例化;
       2
、包含未实现的方法声明;
        3
、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);

       另外,接口有如下特性:
接口除了可以包含方法之外,还可以包含属性、索引器、事件,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。

 

抽象类和接口的区别:
      1.
类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类.而接口只是一个行为的规范或规定,微软的自定义接口总是后带able字段,证明其是表述一类类我能做。。。”.抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中
      2.
接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法
    
      3.
一个类一次可以实现若干个接口,但是只能扩展一个父类
     
      4.
接口可以用于支持回调,而继承并不具备这个特点
.     
      5.
抽象类不能被密封。
   
      6.
抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的

      7.
(接口)与非抽象类类似,抽象类也必须为在该类的基类列表中列出的接口的所有成员提供它自己的实现。但是,允许抽象类将接口方法映射到抽象方法上。
   
      8.
抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的座位子类去实现。
   
      9.
好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染。   
     10.
尽量避免使用继承来实现组建功能,而是使用黑箱复用,即对象组合。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中某一类,就必须把他们全部加载到栈中!后果可想而知.(结合堆栈原理理解)。同时,有心的朋友可以留意到微软在构建一个类时,很多时候用到了对象组合的方法。比如asp.net中,Page类,有Server Request等属性,但其实他们都是某个类的对象。使用Page类的这个对象来调用另外的类的方法和属性,这个是非常基本的一个设计原则。
   
     11.
如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法.

http://blog.csdn.net/bmzyDream_007/archive/2009/05/07/4157560.aspx

c++标准的 接口和抽象类  

c++创造者:Bjarne Stroustrup: 
 
            
我在对人们解释这个问题的过程中遇到了很多问题,而且我也一直不能理解为什么让人们理解这个问题是如此困难。自C++出现那天起,就存在着包含数据成员的类和不包含数据成员的类。在过去,人们强调利用一个最基础的设施以及该设施内部的东西来构造软件系统,而那个“最基本的设施”通常就是抽象基类。从80年代中叶到80年代末,那些仅由虚拟函数组合而成的类通常都被称为ABCs(Abstract Base Classes 抽象基类)。1987年,我在C++中加入了纯虚函数的概念,一个纯虚函数必须被其派生类重写。借助此概念,你可以在一个C++类中通过将其成员函数声明为纯虚函数的方法表明该类是一个纯接口类。从那以后,我就一直强调在C++中,有一种主要的使用类的方法就是让该类不包含任何状态,而仅仅作为一个接口。  从C++的角度来看,一个抽象类和一个接口之间没有任何区别。有时,我们习惯使用“纯抽象类”这个词来表示某个类仅仅只含有纯虚函数(不包含任何数据成员),它是抽象类的最常见的形式。当我试图向人们解释这个概念时,我发现如果我不先向他们介绍纯虚函数这个语言中被直接支持的概念,人们就很难接受它。有些人仅仅因为可以在基类中放入一些数据成员,就觉得他们必须这样做。他们这样做,就等于构造了经典的不稳定基类,当然同时也就招致该结构所带来的一切问题。当我向人们介绍C++中直接支持抽象基类的概念时,情况稍微好一些,不过仍然有许多人不能理解它。我认为这是由于我自身的原因所造成的教育上的失败 — 我低估了做这件事的难度。这与早些时候Simula社团在理解新概念上的失败异常相似。有些新概念难以理解,部分原因在于许多人并不是真的想去学习一些全新的东西,他们自以为自己已经知道了答案。而一旦以为自己已经知道了答案,再去学一些新东西就会变得非常困难了。在1991年的《The C++ Programming Language》第二版中,有几个例子描述了抽象类的概念,可不幸的是,我并没有在全书从头至尾都贯穿这个思想。

 
            

C++纯虚函数

一、定义

  纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”
  virtual void funtion1()=0

二、引入原因

  1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
  2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

三、相似概念

  1、多态性
  指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
  a.编译时多态性:通过重载函数实现
  b 运行时多态性:通过虚函数实现。
  2、虚函数
  虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载
  3、抽象类
  包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。

程序举例

  基类:
  class A
  {
  public:
  A();
  void f1();
  virtual void f2();
  virtual void f3()=0;
  virtual ~A();
  };
  子类:
  class B : public A
  {
  public:
  B();
  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;
  return 0;
  }
  f1()是一个普通的重载.
  调用m_j->f1();会去调用A类中的f1(),它是在我们写好代码的时候就会定好的.
  也就是根据它是由A类定义的,这样就调用这个类的函数.
  f2()是虚函数.
  调用m_j->f2();会调用m_j所指的对象中,对应的这个函数.这是由于new的B对象.
  f3()与f2()一样,只是在基类中不需要写函数实现.

接口是一个没有被实现的特殊的类,它是一系列操作的集合,我们可以把它看作是与其他对象通讯的协议。C++中没有提供类似interface这样的关键 字来定义接口,但是Mircrosoft c++中提供了__declspec(novtable)来修饰一个类,来表示该类没有虚函数表,也就是虚函数都是纯虚的,接口中的纯虚函数在继承时都要被实现。利用__declspec(novtable)我们依然可以定义一 个接口。代码例子如下:

 
#include <IOSTREAM>
using namespace std;

#define interface class __declspec(novtable)

interface ICodec
{
public:
    
virtual bool Decode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,
unsigned  int   * pnDstLen);
    
virtual   bool  Encode( char   *  lpDataSrc,unsigned  int  nSrcLen, char   *  lpDataDst,
unsigned  int   * pnDstLen);
};

class  CCodec :  public  ICodec
{
public :
    
virtual   bool  Decode( char   *  lpDataSrc,unsigned  int  nSrcLen, char   *  lpDataDst,
unsigned  int   * pnDstLen)
     {
         cout 
<<   " 解码... "   <<  endl;
        
return   true ;
     }
    
virtual   bool  Encode( char   *  lpDataSrc,unsigned  int  nSrcLen, char   *  lpDataDst,
unsigned  int   * pnDstLen)
     {
         cout 
<<   " 编码... "   <<  endl;
        
return   true ;
     }
};

int  main( int  argc,  char *  argv[])
{
     ICodec 
*  pCodec  =   new  CCodec();
     pCodec
-> Decode(NULL, 0 ,NULL,NULL);
     pCodec
-> Encode(NULL, 0 ,NULL,NULL);
     delete (CCodec
* )pCodec;
    
return   0 ;
}
 


Bill Venners:

我在1991至1996这5年间,几乎一直仅仅使用C++编程。在那时,我认为多重继承唯一目的就是让我能够从多个基类中继承它们各自的数据和函数 — 不管是虚拟函数还是非虚拟函数。那时候,我和我使用C++的同事几乎从未想过可以使用一种不含任何数据而仅包含纯虚函数的类,也就是现在Java中被称为 接口的东西。最近您好像又越来越多地提起了抽象类这个概念,我想问问是不是最近在实验的过程中发现了一些我们以前未曾注意到的对纯接口类进行多重继承的好 处,抑或是您认为我们以前对抽象类重视得不够?

Bjarne Stroustrup:

我在对人们解释这个问题的过程中遇到了很多问题,而且我也一直不能理解为什么让人们理解这个问题是如此困难。自C++出现那天起,就存在着包含数据 成员的类和不包含数据成员的类。在过去,人们强调利用一个最基础的设施以及该设施内部的东西来构造软件系统,而那个“最基本的设施”通常就是抽象基类。从 80年代中叶到80年代末,那些仅由虚拟函数组合而成的类通常都被称为ABCs(Abstract Base Classes 抽象基类)。1987年,我在C++中加入了纯虚函数的概念,一个纯虚函数必须被其派生类重写。借助此概念,你可以在一个C++类中通过将其成员函数声明 为纯虚函数的方法表明该类是一个纯接口类。从那以后,我就一直强调在C++中,有一种主要的使用类的方法就是让该类不包含任何状态,而仅仅作为一个接口。

从C++的角度来看,一个抽象类和一个接口之间没有任何区别。有时,我们习惯使用“纯抽象类”这个词来表示某个类仅仅只含有纯虚函数(不包含任何数 据成员),它是抽象类的最常见的形式。当我试图向人们解释这个概念时,我发现如果我不先向他们介绍纯虚函数这个语言中被直接支持的概念,人们就很难接受 它。有些人仅仅因为可以在基类中放入一些数据成员,就觉得他们必须这样做。他们这样做,就等于构造了经典的不稳定基类,当然同时也就招致该结构所带来的一 切问题。当我向人们介绍C++中直接支持抽象基类的概念时,情况稍微好一些,不过仍然有许多人不能理解它。我认为这是由于我自身的原因所造成的教育上的失 败 — 我低估了做这件事的难度。这与早些时候Simula社团在理解新概念上的失败异常相似。有些新概念难以理解,部分原因在于许多人并不是真的想去学习一些全 新的东西,他们自以为自己已经知道了答案。而一旦以为自己已经知道了答案,再去学一些新东西就会变得非常困难了。在1991年的《The C++ Programming Language》第二版中,有几个例子描述了抽象类的概念,可不幸的是,我并没有在全书从头至尾都贯穿这个思想。

Bill Venners:

使用纯抽象类有什么好处?什么时候我们应该使用纯抽象类而不是使用更为普遍的多重继承?

Bjarne Stroustrup:

最明显的例子就是“多接口、单实现”,这是一种很常见的情况。例如,你的系统也许既需要序列化功能,也需要迭代功能,那么这两个功能都可以接口的形 式利用抽象类提供。然后,如果需要提供一个支持序列化的容器,你只需要让容器类继承序列化抽象类和迭代抽象类就可以了,而这种多重继承的形式已被Java 和C#采纳。

另一种通常需要使用多重继承的情况是仅仅通过多重继承将手头的一些类组合起来。它们每一个都没有特别复杂的语义,将其组合起来完全是出于使用上的方 便。当然,你也可以使用委托的模式来完成这个工作,也就是说,你可以在对象中容纳一个指向真正实现某些功能的对象指针。这种方法虽然也不错,但每当你在间 接对象中添加一个新方法时,你都需要在自己的类中对应地增加一个新方法。这种做法真让人头痛,而且也没有直截了当地表示出原本的想法,维护起来则更是费时 费力。最后一种情况是你需要从两个类中分别继承它们各自的状态。在这种情况下,当这两个类都非常复杂或它们的语义相互影响时,你很容易陷入混乱之中。然而 你可以通过减少过度继承的方法尽量减少这种情况发生的次数,而当你不可避免地需要使用继承时,你可以通过尽量减少过度使用多重继承达到目的,而如果到了连 多重继承都是非要不可的时候,那么你应该尽量回避那些复杂的变数。总的来说,在对一个具体问题建立一个模型时,你应该让该模型尽量简单,但不致于过分简 单。

有些人经常会说他并不需要多重继承,因为所有多重继承能做的事情都能通过单继承完成,只是要使用我上面提到的那个名为“委托”的小技巧而已。更进一 步,你也并不需要任何继承,因为所有单继承能够完成的事都可以通过类之间的转发完成。实际上,你根本不需要任何类,因为你完全可以利用指针和数据结构来达 到目的。可为什么你会想要建立类呢?什么时候使用语言内建设施比较方便?什么时候你宁愿用一种绕弯的方法呢?我见过有很多场合多重继承甚至是非常复杂的多 重继承发挥了重要作用。总体上来说,我更喜欢使用语言提供的功能来处理事情。

我们应对复杂情形的另外一种方法是利用模板进行组合。具体而言就是提供多个模板参数,而每个参数都是一个完全独立的类,它们都是你能够进行组合的抽 象的具体实现。这些类每一个都是完全独立的,只有最后的派生类才与它们中的每一个存在依赖关系。有时候在一个模板内部根据继承关系进行组合是很便捷的,而 有时则需另想办法(例如你可以将每一个单独的类作为一个数据成员存储或仅存储它们各自的指针)。这里有一个你有时需要从多个类中继承状态的例子:你有一个 配置器对象,它知道如何处理关于内存的分配和销毁的问题,你也有一个存取器对象,只要你把内存地址给它,它就能处理关于内存存取的问题。现在,你准备将他 们都用于你的一个项目实现中,就让我们假设是一个操作矩阵的复杂函数吧,此时你至少已经拥有了两个状态量,可是并没有带来那些对多重继承心存疑虑的人所担 心的那些问题。基本上,你用一些非常简单的词汇就可以将运作的情况解释清楚。

http://blog.sina.com.cn/s/blog_4d0d56900100f8ie.html

0

阅读 (63)   评论  (0) 收藏 (0)  转载原文     打印 举报
已投稿到:
 圈子

登录名: 密码: 找回密码 注册

    

验证码: 请点击后输入验证码 收听验证码

发评论

以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

新浪BLOG意见反馈留言板 不良信息反馈 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

新浪公司 版权所有


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值