Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Interfaces in C++

0.00/5 (No votes)
6 Jun 2005 1  
How to define and use interfaces in C++.

Abstract

The distinction between classes and interfaces is a powerful language feature present both in Java and C# (along with many other languages) but not in C++. For years, our team has been using a "methodological" implementation of the interface concept for C++, which I present here. Starting in VS7, there's also an MS extension pointing in the same way, allowing the compiler to enforce most of the defining characteristics of an interface, and of course, managed extensions for C++ supports the definition and implementation of .NET interfaces too. However there are some subtle and not so subtle differences between each of these mechanisms that you should take into account.

Background

An interface describes the behavior or capabilities of a class without committing to a particular implementation. It represents a contract between a provider and its users, defining what it's required from each implementer, but only in terms of the services they must provide, no matter how they manage to do it.

In case you are unfamiliar with the concept of interfaces, and when and how to use it to improve your design, the following MSDN article might be of great help. It's addressed to Visual Basic users switching to VB.NET, but it does a very good job at explaining the basic ideas, which apply to any object oriented language anyway.

Introduction

Many years ago, around November 1999, I defined a way to declare interfaces for C++, and to make classes implement them, just by using a few macros and making sure some basic rules were followed. I should make it clear that I don't claim to be the "inventor" of this technique or anything like that. Although it was independently developed, mostly with the invaluable advice of Roberto Lublinerman, I later found many articles on the internet, which described more or less the same ideas, and at least some of them were dated way before we had even come to think about it.

These days, the solution can be improved by making use of Microsoft extensions that were introduced in later versions of the C++ compiler, but let's go one step at a time.

The first version

First, some macros are defined in a header file, which you'll probably want to include in your precompiled headers:

//

// CppInterfaces.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

Using these macros, you can declare an interface in the following way:

//

// IBar.h

//


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

Then you can declare a class that implements this interface with something like:

//

// 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
   {
      // stuff...

   }

   virtual void SetBarData(int nData)
   {
      // stuff...

   }
};

Easy, isn't it? Without much effort you are now able to use interfaces in C++. However, as they aren't directly supported in the language, you are supposed to follow some rules, which can't be automatically enforced at compilation time. After all, all that the compiler can see is the use of plain old multiple inheritance and abstract base classes. Here are the rules you need to follow, along with some recommendations:

  • When declaring a class, use the first base class for "structural" inheritance (the "is a" relationship) if there is one, as you normally do. (E.g.: CFrameWnd derives from CWnd, CBitmapButton derives from CButton, YourDialog derives from CDialog, and so on.) This is especially important if you are deriving from MFC classes; declaring them as the first base class avoids breaking MFC's RuntimeClass mechanism.
  • Use additional base classes for interface implementation, as many as you want or need. (E.g.: class Foo : public BasicFoo, implements IBar, implements IOther, implements IWhatever, ...)
  • Do not declare any member variables inside the interfaces. Interfaces are intended to express behavior, not data. Besides, this helps to avoid some problems if you were to use multiple "structural" inheritance and happened to be deriving more than once from the same interface.
  • Declare all the member functions in the interfaces as virtual pure (i.e.: with "= 0"). This ensures every instantiable class that declares to implement an interface does it for all its functions. It's OK to partially implement an interface in an abstract class (in fact it will be abstract anyway if you do so) as long as you implement the remaining functions in the derived classes you actually intend to instantiate. Since the interfaces offer no "basic" implementation, you need to be sure everyone that receives a pointer to some interface will be able to call any of its members; declaring all your interface members as virtual pure will enforce this at compile time.
  • Do not derive your interfaces from anything but other interfaces. You can use the DeclareBasedInterface() macro for that. Normal classes can choose to implement the basic interface or the derived (extended) one; the latter of course means implementing both.
  • Assigning a pointer to a class that implements some interface to a pointer to that interface requires no casting, as you will be actually casting to a base class. Doing it the other way though (from an interface pointer to a pointer to the class implementing it), will require an explicit cast, as you will be casting to a derived class. Since you will in fact be using multiple inheritance (even if for virtually every other practical need you can think of it as single inheritance plus interface implementations), these casts can't be done the "old" way, because they may need different actual memory values. However, enabling Run-Time Type Information (/GR compiler option) and using dynamic casts works fine and it's of course safer anyway.
  • Further, the use of dynamic_cast will give you a way to ask any object or interface whether it implements a given interface or not.
  • You need to be careful to avoid name conflicts between functions in different interfaces, as it isn't easy to detect and resolve those conflicts should you have a class implementing both.

Evaluation

When I posted about the above technique in my recently created blog, one of the readers expressed some concerns in the following terms:

  • Why bother with the macros? They don't enforce anything, and they don't improve readability any more than these old standbys:
    #define begin { 
    #define end } 
  • Maybe that's useful as a crutch for Java/C# developers, but if a developer needs a crutch like this they have other problems.

If you look closely at the DeclareInterface and DeclareBasedInterface macros, you�ll note that there is at least something that is being enforced there: every class implementing an interface will have a virtual destructor. You may or may not think this is important, but there are cases when the lack of a virtual destructor will cause problems. Consider the following code, for instance:

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;
      }
   }
};

As you can see, there�s a factory to which you can ask to create an IBar implementation, depending on a BarType parameter. After using it, you are expected to delete the object; so far so good. Now consider how this is used in the main function of some application:

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!

}

What happens at the delete pBar line depends on whether the actual class of that object has a virtual destructor or not. If Foo doesn�t have a virtual destructor, the compiler will only generate a call to IBar�s implicit empty destructor, the destructor for Foo won�t be called, and thus you�ll have a memory leak. The virtual destructors in the interface declaration macros are there in order to avoid this situation; they ensure every class implementing an interface will also have a virtual destructor.

Now, if we are going to use DeclareInterface, it kind of makes sense to also use EndInterface to match with it, rather than a closing brace with no apparent opening one to match. The need for the Interface and implements macros, which resolve to class and public, respectively, could be objected as superfluous, but I find them to better express the actual intent of the code. If I write Foo : public IBar you can only read some inheritance there and nothing else. If on the other hand, I write Foo implements IBar, you can see it for its actual value and intent: the implementation of the interface concept, and not just any kind of class inheritance. I do find value in that.

To be fair, I�m sure that the reader was actually concerned with the other things these macros do not and cannot enforce: interfaces are expected to have only pure virtual functions and no instance data. But at least, if you write your interfaces using DeclareInterface and EndInterface, the inclusion of any instance data or non virtual pure function should be easy to spot. In Joel Spolsky�s words, these macros also help by �making wrong code look wrong�.

C++ interfaces support in VS7

The folks at Microsoft must have felt the same need to enforce those restrictions on classes used as interfaces, judging from the introduction in VS7 of the __interface keyword as a new Microsoft extension to the C++ compiler. In the documentation they define a Visual C++ interface as follows:

  • Can inherit from zero or more base interfaces.
  • Cannot inherit from a base class.
  • Can only contain public, pure virtual methods.
  • Cannot contain constructors, destructors, or operators.
  • Cannot contain static methods.
  • Cannot contain data members; properties are allowed.

And they note: �A C++ class or struct could be implemented with these rules, but __interface enforces them.� So, if you are not worried by portability, you could use this extension and have the compiler enforce what needs to be enforced, right? Wrong.

Remember that bit about the need for virtual destructors? __interface does not add a virtual destructor for the implementing class, and I can understand why MS didn�t see the need for virtual destructors in interfaces. If you look at the samples in the documentation, it�s pretty clear from where they are COMing. They obviously created this extension with COM interfaces in mind and you never use delete with them, because they are count referenced objects, which call delete on themselves when the count reaches zero.

Couldn�t we just use __interface in the DeclareInterface macro definition, so as to have the best of both worlds? Hmm..., read again the definition: �cannot contain constructors, destructors, or operators�. This led me to initially think it wasn't possible, and even to say that while __interface might be useful for COM interfaces, it was neither intended nor suitable for the general purpose interfaces we are discussing here. Fortunately, that's not the end of the story.

The best of both worlds

A while later I found a rather simple solution: the name for the desired interface is actually used to declare a class that contains a virtual destructor and inherits from an __interface that contains the needed methods. The macros are now defined as follows:

//

// CppInterfaces2.h

//


#define Interface class

#define implements public

#define DeclareInterface(name) __interface actual_##name {

#define DeclareBasedInterface(name, base) __interface actual_##name \
     : public actual_##base {

#define EndInterface(name) };                \
     Interface name : public actual_##name { \
     public:                                 \
        virtual ~name() {}                   \
     };

Here is how you declare an interface using the above defined macros:

//

// IBar2.h

//


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

As you may have noticed, these new macro definitions require using the name of the wanted interface (i.e.: IBar) twice, first for DeclareInterface(), and then for EndInterface(). This introduces an always undesired redundancy, which made me struggle for a while trying to eliminate, but failed. If anyone finds a way to define the macros so as to avoid requiring the same name twice, please let me know.

On the other hand, provided you don�t mind sacrificing portability to anything but MS compilers starting from VS7, the new macros have many advantages over their predecessors, simply because every requirement for an interface (only pure virtual methods, no data members, virtual destructor for implementing classes) is now automatically enforced. You don�t even need to explicitly declare interface methods as virtual or pure virtual (�= 0�), although the compiler won�t complain if you do so.

Additional reading

Before closing this article, I�d like to include some links to related resources, which I found while doing research on this topic.

The techniques described here allow defining and implementing interfaces in C++ through the use of abstract base classes. Some people don�t like this as a solution because it forces every implementing class to have (and use) a virtual table and they find this unacceptable due to the space (the virtual table for each class) and performance (the indirection in each method call) penalties it imposes. As an alternative, Brian McNamara and Yannis Smaragdakis from the Georgia Institute of Technology wrote a paper Static interfaces in C++, which was published in the First Workshop on C++ Template Programming, Erfurt, Germany, October 10 2000.

Christopher Diggins wrote a proposal for a modification of the C++ language, in order to make it support interfaces without virtual functions. I have no idea whether this proposal was actually presented or considered by the standard committee.

Dr. Dobb's Journal August 1998, includes an article by Fred Wild, Keeping interfaces and implementations separate, which discusses some ways to do so in C++ code.

In the comments after my first submission, user Nemanja Trifunovic pointed me to another alternative for static interfaces, the Boost Interface Library[^].

There's a very interesting interview to Scott Meyers, made by Bill Venners on December 2002, in which they discuss the topic of interfaces in C++.

Last but not the least, the father of C++ Bjarne Stroustrup himself, made interesting comments about interfaces and C++ in an interview by Bill Venners on November 2003.

History

  • 6th June, 2005: Minor changes in sample code.
  • 1st June, 2005: Added links to the Boost Interface library and the interview to Scott Meyers.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here