Interfaces
--
02 Oct 2008
Believe it or not you actually
can do interfaces in C++ (well, almost). The syntax is a little funny, but once you get past that it's exactly like a java interface. For those in the C++ know it's defining a class with all pure virtual functions, and then any number of subclasses that implement each of the methods.
Step 1: create the interface header. By convention (at our comapny at least) you name it like so:
IMyClass.h
#ifndef IMYCLASS_H_
#define IMYCLASS_H_
class IMyClass
{
public:
virtual ~IMyClass() {};
virtual bool getValue() const=0;
virtual void setValue(bool)=0;
};
#endif /*IMYCLASS_H_*/
Some funky things to note about the syntax. It's the
=0 at the end of each of the method names that tells the compiler that there is no implementation for this method at this time (i.e. it's abstract [pure virtual]).
An aside: the const keyword after the method declaration means that the method is not allowed to change any state.
Step 2: create an implenting header file. By contention it is:
MyClass.h
#ifndef MYCLASS_H_
#define MYCLASS_H_
#include "IMyClass.h"
class MyClass : public IMyClass
{
public:
bool getValue() const;
void setValue(bool);
protected:
bool m_value;
};
#endif /*MYCLASS_H_*/
Here we're extending the interface with a concrete class definition (MyClass), and we're not using virtual because we're not planning to subclass this class (in fact if you don't implement each pure virtual method from above, this class will also be abstract and you won't be able to instantiate it).
Aside #2: You can have multiple public/private/protected sections, and the order doesn't matter.
ICK. My convention is to have one section of each, with public on top, protected next, and private on the bottom. Another company convention is to use
m_ for member variables. The typical java equivalent is just an underscore.
Step 3: implement the class. Create the (you guessed it)
MyClass.cpp file.
#include "MyClass.h"
bool MyClass::getValue() const
{
return m_value;
}
void MyClass::setValue(bool val)
{
m_value = val;
}
Aside #3: There was much debate as to whether you should also #include the "IMyClass.h" file. You don't
need to, but some thought you
should. My personal opinion was that it's not necessary since MyClass.h is implementing IMyClass.h and it's not going to suddenly stop #including IMyClass.h itself since it's part of the contract.
What good is an interface if you only have one implementation? Let's create a second implementation. You use the same IMyClass.h file, but in this case we'll create
MyOTherClass.h/cpp files. The only difference will be that the getValue will return the inverse of the actual value.
MyOtherClass.h
#ifndef MYOTHERCLASS_H_
#define MYOTHERCLASS_H_
#include "IMyClass.h"
class MyOtherClass : public IMyClass
{
public:
bool getValue() const;
void setValue(bool);
protected:
bool m_value;
};
#endif /*MYOTHERCLASS_H_*/
MyOtherClass.cpp
#include "MyOtherClass.h"
bool MyOtherClass::getValue() const
{
return !m_value;
}
void MyOtherClass::setValue(bool val)
{
m_value = val;
}
Finally, let's show an example of actually using our two interfaces. I've created a small
main.cpp class:
#include <iostream>
#include "MyClass.h"
#include "MyOtherClass.h"
using namespace std;
int main()
{
MyClass mc;
MyOtherClass mc2;
mc.setValue(false);
mc2.setValue(false);
cout << "mc value is: " << mc.getValue() << endl;
cout << "mc2 value is: " << mc2.getValue() << endl;
mc.setValue(true);
mc2.setValue(true);
cout << "mc value is: " << mc.getValue() << endl;
cout << "mc2 value is: " << mc2.getValue() << endl;
return 0;
}
Aside #4: Notice that to instantiate our classes, i just declared the variables? That would never fly in java, but in C++ it's actually calling the default constructor (copy constructor?).
When running the program, you get the following output:
mc value is: 0
mc2 value is: 1
mc value is: 1
mc2 value is: 0