Namespaces
Variants
Actions

Abstract Classes

From cppreference.com


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.

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.

Interface vs Abstract