Abstract Classes
An interface is an implementation independent description of the available access functions to an object. If a part of the program is able to use the interface, it is able to work with all classes inheriting the interface without knowing those classes in detail.
For example a human driver is able to use the interface CarInterface
. Every car that implements the CarInterface
can be operated by the driver. The driver doesn't need to know any details about the car itself as long as it only uses the functionality provided by CarInterface
.
Contents |
Pure virtual
In C++ you are able to declare pure virtual (member) functions which are a special kind of virtual functions and are denoted by =0
at the end of the virtual function declaration.
class Base { public: virtual ~Base() {} // As always, you want the destructor to be virtual when virtual functions are present. virtual int standard(int n) { return n; }; // Standard virtual function virtual int pure(int n) = 0; // Pure virtual function };
What differentiates pure()
from standard()
is that the inheriting class is required to implement the pure virtual function. It's optional in case of standard()
.
// Error: pure() not implemented. class DerivativeV1 : public Base {}; // OK: Implements pure() and uses the same implementation of standard() as defined in Base. class DerivativeV2 : public Base { public: int pure() { return 42; } }; // Error: Still doesn't implement pure(). class DerivativeV3 : public Base { public: int standard() { return 1; } }; // OK: pure() is implemented and standard() is overridden. class DerivativeV4 : public Base { public: void standard() { return 1; } void pure() { return 42; } };
C++ does not prohibit Base
from providing a definition for the pure virtual function. However, the derived class still has to implement the pure virtual function. Definition of the pure virtual function has to be placed outside the declaration (in header or source file - doesn't matter).
class Base { public: virtual ~Base() {} virtual int standard() { return 0; }; virtual int pure() = 0; }; // Implementation of the pure virtual function. int Base::pure() { return 7; } // Error: Doesn't implement pure(); class DerivativeV1 : public Base {}; // OK: pure() implemented and this implementation will be used when calling pure(). class DerivativeV2 : public Base { public: int pure() { return 42; } }; // OK: pure() implemented and uses the implementation of Base class pure(). class DerivativeV3 : public Base { public: int pure() { return Base::pure() + 15; } };
As presented in the case of DerivativeV3
, you are able to use the implemented pure()
method from the base class, but only in derived classes, as you won't be able to create object of Base
which will be discussed in the next section.
Abstract classes
Abstract classes are classes which either:
- declare a pure virtual function (providing an out-of-declaration definition doesn't change anything in this regard), or
- leave at least one of the inherited pure virtual function unimplemented
Abstract classes (apart from pure virtual functions) can have member variables, non-virtual functions, regular virtual functions, static functions, etc.
Objects of abstract classes cannot be insantiated. This is because an abstract class is considered only as a concept/partial specification, not a full-fledged class which can be used "out of the box."
class PureVirtual { public: virtual ~Base() {} virtual int pure() = 0; private: int x; // Look, I can have member variables. }; class InheritedPureVirtual : public PureVirtual { }; class PureVirtualWithImplementation { public: virtual ~PureVirtualWithImplementation() {} virtual int pure() = 0; }; int PureVirtualWithImplementation::pure() { return 0; } class PureVirtualDouble { public: virtual ~PureVirtualDouble() {} virtual int pure() = 0; virtual void pure2(float) = 0; }; class AlmostValid : public PureVirtualDouble { public: int pure() { return 42; } // Notice no implementation of pure2(float). }; class Valid : public PureVirtual { public: int pure() { return 42; } }; // C++ creates a default constructor for the above classes, // so we don't have to specify it ourselves. In case you // wonder how come a class cannot be constructed without // constructor being defined. int main() { // Fails to compile (cannot create an abstract object). PureVirtual pv; InheritedPureVirtual ipv; PureVirtualWithImplementation pvwi; AlmostValid av; // Successfully creates the object. Valid v; }
The reasons for creating abstract classes will be discussed in the next section.
Java and C# require usage of the keyword abstract when defining abstract classes. There is no such keyword in C++. The compiler computes the 'abstractness' itself, checking whether any pure virtual functions are present. |
Interface classes
Interface classes in C++ are abstract classes which consist only of pure virtual functions, which makes them - one might say - "super abstract". As we already learned in the previous section you can't even create an abstract class object, so what is the reason of their existence?
The answer is: polymorphism. Interfaces define the program architecture via inheritance hierarchy serving as the base classes for all implementations. To see their benefits, let's look at an example.
This section is incomplete Reason: Add multiple interfaces example |
Interface vs Abstract
This section is incomplete Reason: Show why interfaces are generally preferable to abstract classes |