面向对象的语言诸如JAVA提供了Interface来实现接口,但C++却没有这样一个东西,尽管C++ 通过纯虚基类实现接口,譬如COM的C++实现就是通过纯虚基类实现的(当然MFC的COM实现用了嵌套类),但我们更愿意看到一个诸如 Interface的东西。下面就介绍一种解决办法。

首先我们需要一些宏:

//
// Interfaces.h
//

#define Interface class

#define DeclareInterface(name) Interface name { \
          public: \
          virtual ~name() {}

#define DeclareBasedInterface(name, base) class name :
        public base { \
           public: \
           virtual ~name() {}

#define EndInterface };

#define implements public

有了这些宏,我们就可以这样定义我们的接口了:

//
// IBar.h
//

DeclareInterface(IBar)
   virtual int GetBarData() const = 0;
   virtual void SetBarData(int nData) = 0;
EndInterface

是不是很像MFC消息映射那些宏啊,熟悉MFC的朋友一定不陌生。

现在我们可以像下面这样来实现我们的接口了:

//
// Foo.h
//

#include "BasicFoo.h"
#include "IBar.h"

class Foo : public BasicFoo, implements IBar
{
// Construction & Destruction
public:
   Foo(int x) : BasicFoo(x)
   {
   }

~Foo();

// IBar implementation
public:
   virtual int GetBarData() const
   {
      // add your code here
   }

virtual void SetBarData(int nData)
   {
      // add your code here
   }
};

怎么样,很简单吧,并不需要做很多的努力我们就可以在C++中使用接口了。然而,由于这并不是语言本身所直接支持的特性,所以我们需要遵循一些规则:
         a)   声明一个类的时候,如果你的类除了要从接口类继承外还要从另一个类继承(结构上的继承,即is a关系),则把这个类作为第一个基类,就像我们平时做的一样,譬如CFrameWnd从CWnd继承,CBitmapButton从CButton继承,CMyDialog从CDialong继承。当你要从MFC类派生的时候,这尤其重要,把他们声明为第一个基类以避免破坏MFC的RuntimeClass机制。
         b)   其他的基类紧跟其后,有多少就跟多少,如果你需要的话。譬如:class Foo : public BasicFoo, implements
IBar, implements
IOther, implements
IWhatever, ...
         c)   接口类里面不要声明任何成员变量。接口类仅用于描述行为而不是数据。当你要作多重继承时,这样做可以避免数据成员被从同一个接口类多次继承。
         d)   接口类的所有成员函数定义为纯虚函数。这可以确保你的实现类来实现这些函数的全部,当然你也可以在抽象类实现部分函数,只要在你的派生类里实现剩下的函数。
         e)   不要从除了接口类的其他任何类派生你的接口类。DeclareBasedInterface()可以做到这个.普通类可以选择实现基接口还是派生的接口,后面一种意味着两者都要实现。
   f)
 将一个指向实现接口的类的指针赋值给一个指向该接口类的指针是不需要强制类型转换的,但反过来将一个接口类的指针赋值给一个实现该接口的类的指针就需要
一个显式的强制类型转换。事实上我们可能会使用多重继承,这样这些转换我们就不能使用老式的转换。不过使用运行时类型信息(使用/GR选项)和动态类型转
换可以很好的工作当然也更安全。
   g) 此外dynamic_cast为你提供了一种查询一个对象或接口是否实现了一个指定的接口的途径。
   h) 你还要非常小心的避免不同接口函数的命名冲突。

如果你仔细观察DeclareInterface 和 DeclareBasedInterfaca宏你会发现有一个操作是必须的:每个接口类都有一个虚析构函数。你可能认为这不重要,但是如果没有这个就可能会导致一些问题,看看下面的例子:就像你看到的一样,这里有一个类工厂,它根据BarType来创建一个IBar的实现,当你使用完以后你当然希望要delete该对象,你会像下面这样做:

int main()
{
   IBar* pBar = BarFactory::CreateBar(Foo);

pBar->SetName("MyFooBar");
   // Use pBar as much as you want,
   // 

// and then just delete it when it's no longer needed
   delete pBar;    // Oops!
}

delete
pBar 做了什么取决于该对象是否有一个虚析构函数。如果Foo没有一个虚析构函数,则只有IBar
的隐式的空析构函数被调用,Foo的析构函数不会被调用,这样就发生了内存泄露。接口类里虚析构函数的声明避免了这用状况,它确保每个实现接口的类都有一
个虚析构函数。

当你使用DeclareInterfac的时候,记得使用EndInterface和它匹配。Interface 宏和 implements宏仅仅是代替了class和public,这看起来是多余的,但我认为它们更明确的表达了代码的意图。如果我这么写:class Foo : public IBar,你可能认为这只是一个简单的继承;但如果我这么写:class Foo: implements IBar,你就会看到它实际的价值和意图---这是对一个接口的实现,而不是简单的一次继承。
DeclareInterface(IBar)
   virtual LPCTSTR GetName() const = 0;
   virtual void SetName(LPCTSTR name) = 0;
EndInterface

class Foo : implements IBar
{
// Internal data
private:
   char* m_pName;

// Construction & Destruction
public:
   Foo()
   {
      m_pName = NULL;
   }

~Foo()
   {
      ReleaseName();
   }

// Helpers
protected:
   void ReleaseName()
   {

if (m_pName != NULL)
         free(m_pName);
   }

// IBar implementation
public:
   virtual const char* GetName() const
   {
      return m_pName
   }

virtual void SetName(const char* name)
   {
      ReleaseName();
      m_pName = _strdup(name);
   }
};

class BarFactory
{
public:
   enum BarType {Faa, Fee, Fii, Foo, Fuu};

static IBar CreateNewBar(BarType barType)
   {
      switch (barType)
      {
         default:
         case Faa:
            return new Faa;
         case Fee:
            return new Fee;
         case Fii:
            return new Fii;
         case Foo:
            return new Foo;
         case Fuu:
            return new Fuu;
      }
   }
};

C++中使用接口的更多相关文章

  1. 通过拦截器Interceptor实现Spring MVC中Controller接口访问信息的记录

    java web工程项目使用了Spring+Spring MVC+Hibernate的结构,在Controller中的方法都是用于处理前端的访问信息,Controller通过调用Service进行业务 ...

  2. 【总结】浅谈JavaScript中的接口

    一.什么是接口 接口是面向对象JavaScript程序员的工具箱中最有用的工具之一.在设计模式中提出的可重用的面向对象设计的原则之一就是“针对接口编程而不是实现编程”,即我们所说的面向接口编程,这个概 ...

  3. 介绍Unreal Engine 4中的接口(Interface)使用C++和蓝图

    这个教程是从UE4 Wiki上整理而来. 在C++中直接使用Interface大家应该很熟悉.只是简单先定义一个个有虚函数的基类,然后在子类中实现相应的虚函数.像这样的虚函数的基类一般概念上叫接口.那 ...

  4. Myeclipse中打开接口实现类的快捷键

    Myeclipse中打开接口实现类的快捷键-----Ctrl + T Myeclipse中 Open Type快捷键-----Ctrl + Shift + T

  5. 在网页程序或Java程序中调用接口实现短信猫收发短信的解决方案

    方案特点: 在网页程序或Java程序中调用接口实现短信猫收发短信的解决方案,简化软件开发流程,减少各应用系统相同模块的重复开发工作,提高系统稳定性和可靠性. 基于HTTP协议的开发接口 使用特点在网页 ...

  6. Android中的接口回调技术

    Android中的接口回调技术有很多应用的场景,最常见的:Activity(人机交互的端口)的UI界面中定义了Button,点击该Button时,执行某个逻辑. 下面参见上述执行的模型,讲述James ...

  7. Spring中Ordered接口简介

    目录 前言 Ordered接口介绍 Ordered接口在Spring中的使用 总结 前言 Spring中提供了一个Ordered接口.Ordered接口,顾名思义,就是用来排序的. Spring是一个 ...

  8. 面向对象编程语言中的接口(Interface)

    在大多面向对象的编程语言中都提供了Interface(接口)的概念.如果你事先学过这个概念,那么在谈到“接口测试”时,会不会想起这个概念来!?本篇文章简单介绍一下面向对象编程语言中的Interface ...

  9. C#中的接口

    C#中的接口(转) 转自:http://www.cnblogs.com/zhenyulu/articles/377705.html 本文中所有图示纯为个人理解(参考了Assembly中元数据的存储方式 ...

  10. Java和C#中的接口对比(有你不知道的东西)

    1.与Java不同,C#中的接口不能包含字段(Field). 在java中,接口中可以包含字段,但是这些字段隐式地是static和final的.而C#不允许接口中有字段,编译器在编译时就会提示错误(如 ...

随机推荐

  1. Session随便写的(抄书笔记)

    会话是web开发中常用的一种对象.会话是存在于服务器端的对象,因此会话超时是保证性能效率的必要手段,本章将学习几种常用的使会话失效的办法.大多数容器都使用cookie作为会话跟踪的基础,但是cooki ...

  2. php数组排序函数

    下边提到的几个数组函数的排序有一些共性: 1 数组被作为排序函数的参数,排序以后,数组本身就发生了改变,函数的返回值为bool类型.2 函数名中出现单a表示association,含义为,在按值排序的 ...

  3. Linux技巧:一次删除一百万个文件最快方法

    昨天,我看到一个非常有趣的删除一个目录下的海量文件的方法.这个方法来自http://www.quora.com/How-can-someone-rapidly-delete-400-000-files ...

  4. tcp的精髓:滑动窗口

    TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现.一.滑动窗口协议 关于这部分自己不晓得怎么叙述才好,因为理解的部分更多, ...

  5. Android GLSurfaceView用法详解(二)

    输入如何处理       若是开发一个交互型的应用(如游戏),通常需要子类化 GLSurfaceView,由此可以获取输入事件.下面有个例子: java代码: package eoe.ClearTes ...

  6. 【转】VMware 11安装Mac OS X 10.10

    VM11安装Mac OS X 10.10 网上竟没有搜到相似的内容,所以拿出来大家分享 工具/原料 1.VMware Workstation 11 2.unlocker 203(for OS X 插件 ...

  7. org-mode

    org-mode 编辑   目录 1简介 2扩展     1简介编辑 Org-模式(Org-mode)是文本编辑软件Emacs的一种支持内容分级显示的编辑模式.这种模式支持写 to-do 列表,日志管 ...

  8. linux log4j 使用

    1.首先到Apache官网下载log4j.jar文件http://logging.apache.org/log4j/1.2/download.html 引用到eclipse项目里面 2.在src目录下 ...

  9. yii 操作session和cookie

    一,在Yii中使用session 1,CHttpSession 与原生态php5的session使用差别是,php5使用session_start();$_session['key'] = $valu ...

  10. python内建函数sorted方法概述

    python中,具体到对list进行排序的方法有俩,一个是list自带的sort方法,这个是直接对list进行操作,只有list才包含的方法:另外一个是内建函数sorted方法,可以对所有可迭代的对象 ...