Tuesday, 15 April 2014

Final (non inheritable) class

There are often two forced actions when coming to decide if inherit from classes. One is to force to inherit and another is to force not to inherit. The first case can be achieved by pure virtual function. By declaring any function (except constructor) as pure virtual function this class will be not be able to be instantiated. And only its derived classes can be instantiated. The other case is a bit of complicated because so far in C++03 there is no keyword (attribute) to force this concept/option, like keyword "sealed" in Java. Hence some techniques and design patterns are used to achieve this. However in C++11 new feature (class attribute) is introduced to solve this problem, which is much neater than C++03's workarounds.

1. Implementation of final class in C++03
Based on the initialization and destruction order, (see my other blog entry http://cpluspluslearning-petert.blogspot.co.uk/2014/03/the-order-of-object-initializationdestr.html), of object the constructor and destructor of base class have to be accessible by the derived class in order that the derived class can be allocated/released properly. So the common technique to prevent a class from being inherited is to declare its constructor or/and destructor inaccessible to anyone, even its derived classes. Therefore the constructor or/and destructor have to be declared as "private".

At the same time we need to provide a way to make this class useful to others, which means that it must provide a way to others to make sure that its object can be instantiated. Hence either a creation method (be able to call/access the constructor) should be provided if the constructor is declared as "private", or a destruction method is provided if the destructor is declared as "private". The technique for this issue is to provide static creation/destruction function for this class or provide a friend class/function to access its constructor or/and destructor.

Solution 1:
- static creation and destruction methods (factory method pattern)
- private constructor and destructor

*********************************************************************************
class FinalClass {
public:
    static FinalClass* CreateFinalClass(/*arguments for constructor*/);
    static void Destroy(FinalClass*);

    /*
     * other public functions declearation
     */
private:
    FinalClass(/*arguments*/);
    ~FinalClass();
   
    // the copy constructor and assignment operator
    // do not have to be private
    FinalClass(const FinalClass&);
    FinalClass& operator=(const FinalClass&);
};
*********************************************************************************

The creation and destruction methods are provided as public and static functions, which have the access to FinalClass's constructor and destructor and can be direclty accessed from the outside of this class.The users can use these two functions to create and destroy the objects.

Solution 2:
- friend creation and destruction method
- private constructor and destructor

*********************************************************************************
class FinalClass {
public:
    /*
     * other public functions declearation
     */
   
    friend FinalClass* CreateFinalClass(/*arguments for constructor*/);
    friend void Destroy(FinalClass*);  
private:
    FinalClass(/*arguments*/);
    ~FinalClass();
   
    // the copy constructor and assignment operator
    // do not have to be private
    FinalClass(const FinalClass&);
    FinalClass& operator=(const FinalClass&);
};

// standalone function
FinalClass* CreateFinalClass(/*arguments for constructor*/);
void Destroy(FinalClass*);
*********************************************************************************

These two standalone functions are declared as friends of FinalClass. Therefore they have the access to FinalClass's private parts, such as its constructor and destructor. And they can be used by the users to create and destroy the objects.

Solution 3: 
- friend builder class (Builder pattern)
- private constructor

*********************************************************************************
class FinalClass {
public:
    ~FinalClass();
    /*
     * other public functions declearation
     */
   
    friend class FinalClassBuilder;  
private:
    FinalClass(/*arguments*/);
   
    // the copy constructor and assignment operator
    // do not have to be private
    FinalClass(const FinalClass&);
    FinalClass& operator=(const FinalClass&);
};

class FinalClassBuilder {
public:
    FinalClassBuilder();
    ~FinalClassBuilder();
    FinalClassBuilder& GiveArgument1(/*argument 1*/) {
        m_arugment1 = argument1; // psedo-code
        return *this;
    }
   
    FinalClassBuilder& GiveArgument2(/*argument 2*/) {
        m_argument2 = argument2; // psedo-code
        return *this;
    }
   
    /*
     * more arguments here
     */

    FinalClass* CreateFinalClass() {
        return new FinalClass(m_arugment1, m_arugment2,....);
    }
private:
    Argument1 m_argument1;
    Argument2 m_argument2

    /*
     * more arguments here
     */
};

void Foo()
{
    // Create a FinalClass object
    FinalClassBuilder fcb;
    FinalClass* fcPtr = fcb.GivenArgment1(arg1).GivenArgument2(arg2).CreateFinalClass();
   
    /*
     * use FinalClass object fcPtr
     */
 
    delete fcPtr;
}
*********************************************************************************

Here only one condition is forced, - the constructor is declared as private but the destructor is declared as public and can be accessed directly. Again FinalClassBuilder is declared as a friend class of FinalClass. Hence it has full access to private parts of FinalCalls (here its constructor). And it provides functions to specify the argments to create the objects of FinalClass.

2. C++11 - attribute "final" to classes
In C++11 an attribute for the class is introduced as "final". Use this attribute as a appendix in the class declaration. It marks this class is a final class and can't be inherited any more. If any attempt to inherit from it, the compiler will complain about it.

Solution:
*********************************************************************************
class FinalClass final {
public:
    FinalClass();
    ~FinalClass();

    // Copy constructor and assignment operator declaration
    // is irrevalent to this solution. Based on your need
    // they may declared with any access specifier
    // Here I would like to depress the default implemenation
    FinalClass(FinalClass&) = deleted;
    FinalClass& operator=(FinalClass&) = deleted;
};

class Derived : FinalClass { // ERROR: C++11 does not allow to inherit from
                             // a final class

};
*********************************************************************************

With this new attribute "final" introduced, there is no need to declare constructor or/and destructor as "private" any more. This class can be just implemented as any other normal class based on your needs. And the object can be created/destroyed as needed directly from constructor and destructor. Check the solution - how neat it is and how small the code is.

3. Summary
Attribute "final" for the class is absolutely a winner feature introduced in C++11. Absolutely fantastic. It simply maps of the concept of "final" and no other hassle at all.

However the attribute "final" for the virtual function introduced in C++ is absolutely waste in my opinion. Please read my other blog for more details, http://cpluspluslearning-petert.blogspot.co.uk/2014/04/c-features-override-controls.html.

Biobiliograogy:
[1] http://en.wikipedia.org/wiki/Strategy_pattern
[2] GOF, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, "Design Patterns - Elements of Reusable Object-Oriented Software", 1994
[3] http://www.stroustrup.com/C++11FAQ.html

No comments:

Post a Comment