Search code examples
c++classinheritanceumlclass-diagram

How to represent C++ private inheritance in an UML diagram?


In C++, one can create a sub-class via public, protected, or private inheritance. What's the notation to indicate that in a UML class diagram? I'm thinking about putting a label on the arrow but not sure if that's common practice.


Solution

  • What is private inheritance in C++?

    Private inheritance in C++, such as:

    class B1 {
    public: 
       void test(); 
       ...
    };
    class D1 : private B1 {
    public:
       void demo() { test(); }
    ...
    }; 
    

    means that every instance of D1 is an instance of B1, but that is hidden for the outside world. This strange construct aims at implementing the derived class by reusing the code of the base class, but as if there would be no inheritance.

    In consequence, unlike public inheritance, a D1 object could not be used where a B1 object is expected:

     B1 *p = new D1;     //ouch -> error: ‘B1’ is an inaccessible base of ‘D1’
    

    Is this UML inheritance?

    In UML, when a derived class specializes a more general base classe, it means the following:

    Type conformance means that if one Type conforms to another, then any instance of the first Type may be used as the value of a TypedElement whose type is declared to be the second Type. A Classifier is a Type, and conforms to itself and to all of its generalizations.

    So in UML, if D1 specialises (i.e. inherits from) B1, a D1 instance could always be be used in place of a B1 object. This does not match the C++ private inheritance.

    Moreover, there is not either a realization relationship between an interface and its implementations either, since D1 does not conform to the interface defined by B1:

    An InterfaceRealization relationship between a BehavioredClassifier and an Interface implies that the BehavioredClassifier conforms to the contract specified by the Interface by supporting the set of Features owned by the Interface, and any of its parent Interfaces.

    How to represent it in standard UML?

    Obviously, there is a dependency: D1 depends on B1. So you could simply show a dependency. But this does not really help to grasp the kind of relationship and is not very useful. (unless you add a comment to explain)

    A better approach would therefore be to map UML to match the C++ semantics. In this regard, you could envisage to model the private inheritance as a composition relation:

    enter image description here

    Why? Because the situation is very similar (although not completely) to the following composition alternative:

    class B2 {
    public: 
       void test(); 
       ...
    };
    
    class D2 {
    private: 
       B2 base;  // instead of private inheritance
    public:
       void demo() { base.test(); }  // base members accessed via base
    ...
    }; 
    

    So all we do in the UML model here, is to make explicit that in any D1 instance, there is a B1 sub-object not accessible directly from the outside world.

    Edit: Other alternatives?

    In the former and now obsolete UML 1.4, a generalization relationship could have a stereotype «Implementation» that would have fulfilled your need, but is no longer supported since 2005, and might mislead some readers who associate "implementation" with interface:

    Specifies that the child inherits the implementation of the parent (its attributes, operations and methods) but does not make public the supplier's interface, nor guarantee to suport them, thereby violating substituability. This is private inheritance and is usually used only for programming implementation puproposes.

    Digging a little bit in the UML metamodel, it appears that generalization has a isSubstitutable property which is true by default. So you could think of using your own language-specific profile, and define therein a stereotypes «Private inheritance» and «Protected inheritance» for a specialization of the Generalization metamodel element, both with isSubstituable=false. This would allow:

    enter image description here

    This could be a very pragmatic and readable way to convey your language-specific design intent, including that a D1 object is not substituable for B1. But be aware that this is not without risks: isSubsituable is only about run time promises, and has in reality no impact regarding inheritance rules of public features in UML. Automated tools might therefore come to a different conclusion than your readers (this is why I proposed another approach above).