How do I setup a class that represents an interface? Is this just an abstract base class?
class Shape
{
public:
// pure virtual function providing interface framework.
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
int main(void)
{
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
cout << "Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
cout << "Triangle area: " << Tri.getArea() << endl;
return 0;
}
Result: Rectangle area: 35 Triangle area: 17
We have seen how an abstract class defined an interface in terms of getArea() and two other classes implemented same function but with different algorithm to calculate the area specific to the shape.
In C++11 you can easily avoid inheritance altogether:
struct Interface {
explicit Interface(SomeType& other)
: foo([=](){ return other.my_foo(); }),
bar([=](){ return other.my_bar(); }), /*...*/ {}
explicit Interface(SomeOtherType& other)
: foo([=](){ return other.some_foo(); }),
bar([=](){ return other.some_bar(); }), /*...*/ {}
// you can add more types here...
// or use a generic constructor:
template<class T>
explicit Interface(T& other)
: foo([=](){ return other.foo(); }),
bar([=](){ return other.bar(); }), /*...*/ {}
const std::function<void(std::string)> foo;
const std::function<void(std::string)> bar;
// ...
};
In this case, an Interface has reference semantics, i.e. you have to make sure that the object outlives the interface (it is also possible to make interfaces with value semantics).
These type of interfaces have their pros and cons:
Finally, inheritance is the root of all evil in complex software design. In Sean Parent's Value Semantics and Concepts-based Polymorphism (highly recommended, better versions of this technique are explained there) the following case is studied:
Say I have an application in which I deal with my shapes polymorphically using the MyShape
interface:
struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle
In your application, you do the same with different shapes using the YourShape
interface:
struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...
Now say you want to use some of the shapes that I've developed in your application. Conceptually, our shapes have the same interface, but to make my shapes work in your application you would need to extend my shapes as follows:
struct Circle : MyShape, YourShape {
void my_draw() { /*stays the same*/ };
void your_draw() { my_draw(); }
};
First, modifying my shapes might not be possible at all. Furthermore, multiple inheritance leads the road to spaghetti code (imagine a third project comes in that is using the TheirShape
interface... what happens if they also call their draw function my_draw
?).
Update: There are a couple of new references about non-inheritance based polymorphism:
While it's true that virtual
is the de-facto standard to define an interface, let's not forget about the classic C-like pattern, which comes with a constructor in C++:
struct IButton
{
void (*click)(); // might be std::function(void()) if you prefer
IButton( void (*click_)() )
: click(click_)
{
}
};
// call as:
// (button.*click)();
This has the advantage that you can re-bind events runtime without having to construct your class again (as C++ does not have a syntax for changing polymorphic types, this is a workaround for chameleon classes).
Tips:
click
in your descendant's constructor.protected
member and have a public
reference and/or getter.if
s vs. state changes in your code, this might be faster than switch()
es or if
s (turnaround is expected around 3-4 if
s, but always measure first.std::function<>
over function pointers, you might be able to manage all your object data within IBase
. From this point, you can have value schematics for IBase
(e.g., std::vector<IBase>
will work). Note that this might be slower depending on your compiler and STL code; also that current implementations of std::function<>
tend to have an overhead when compared to function pointers or even virtual functions (this might change in the future).