Monday 14 April 2014

C++11 features - Override controls

As my previous blog mentioned that 3 goals are what the programmers are striving to achieve, simplicity, correctness and maintainability in my other blog, http://cpluspluslearning-petert.blogspot.co.uk/2014/04/design-patterns-for-scientific.html. For override control I am going to talk about it from the prospect of maintainability point of view. When the programmers write an application, it is kind of communications between them, to be easy to read, understand, maintain and extend. Any ambiguity will hinder clear and smooth communication. For the current override control on C++03 there are quite a few occasions that can cause ambiguity among the code and bury the mines for potential bugs. Therefore some new instrument is introduced in C++11 to remove ambiguity and give the programmers the exact tools to express themselves in a succinct way.

1. Ambiguity in C++03
In the current C++03 there are a few occasions that cause ambiguity in the code and hence caused the varying understanding among programmers about their intention.
- Deliberately override a function but misspelling the function name
- Deliberately override a function but mis-declaring with different types of argument
- Deliberately name hiding

************************************************************
struct Base {
    virtual void Foo(int);
    virtual void Bar(float);
    virtual std::string GetName() const;
    virtual void Cat();
    virtual void Hat();
    void Golf();
};

struct Derived : public Base {
    virtual void Foo(int); // intended to override: correctly
    virtual void Bar(int); // intended to override: incorrectly
                           // specify with different type of argument
                           // a different type virtual function declared
                           // also hide Base::Bar(float);
    virtual std::string GetName(); // intended to override: incorrectly
                                   // missed the "const"
                                   // a different virtual function
                                   // defined (unconst-function type)
    virtual void Car(); // intended to override: incorrectly
                        // misspelling the function name
                        // then a new virtual function declared
    virtual int Hat(); // intended to override: incorrectly
                       // misuse the wrong return type
                       // ERROR: compilation error
                       // The return type of virtual function has to
                       // identical of covariant
                       // See C++11 standard Chapter 10
    void Golf(int); // intended to name hiding: correctly  
};

void Foo()
{
    Derived d;
    d.Foo(1); // Derived::Foo(int)
    d.Bar(1); // Derived::Bar(int)
    d.Bar(1.0); // Derived::Bar(int)
                // Or introduce naming of Base into Derived
                // "using Base::Bar" - then Base::Bar(float)
                // But this is not intended
    d.Base::Bar(1.0); //Base::Bar(float)
    d.GetName(); // Derived::GetName()
    d.Car(); // Derived::Car()
    d.Cat(); // Base::Cat()
    d.Golf(1); // Derived::Golf(int)
    d.Base::Golf(); // Base::Golf()
                    // Or introduce Base::Golf() into Derived
                    // by "using Base::Golf" in Derived's
                    // declaration

    Base& bRef = d;                  
    bRef.Foo(); // Derived::Foo(int)
    bRef.Bar(1); // Base::Bar(float)
                 // "1" is promoted to "float"
    bRef.Bar(1.0); // Base::Bar(float)
    bRef.GetName(); // Base::GetName() const
    bRef.Car(); // Derived::Car()
    bRef.Cat(); // Base::Cat()
    bRef.Golf(1); // ERROR: Golf() is static-linked function
                  // can't find Golf(int) under Base class
    bRef.Golf(); // Base::Golf()
                 // Golf() is static-liked to Base
}
************************************************************

Under C++03 standard the language can't let the programmers to express what exactly they want. And it all depends on the assumption that the programmers are component developers and the code they written express what exactly they want. And there is no necessary check.

Based on the assumption the compilers have to take granted that all the code above is legitimate and provides no checking against the will of the programmer, even a friendly warning. (Except on the error on the mismatch on the return type). Therefore potentially there is loophole for the software bugs.

2. Override control - attribute "override" in C++11
C++11 introduced an attribute, override, for virtual functions to allow the programmer to express their intention clearly. With "override" attribute appending to the virtual function declaration, the compiler will explicitly check if there is an exactly match in its base class. If a mismatch existing or function not found, an compilation error will be generated.

************************************************************
struct Derived : public Base {
    virtual void Foo(int) override; // intended to override: correctly
    virtual void Bar(int) override; // intended to override: incorrectly
                                    // specify with different type of argument
                                    // a different type virtual function declared
                                    // also hide Base::Bar(float);
                                    // ERROR: compiler will point out
    virtual std::string GetName() override; // intended to override: incorrectly
                                            // missed the "const"
                                            // a different virtual function
                                            // defined (unconst-function type)
                                            // ERROR: compiler will point out
    virtual void Car() override; // intended to override: incorrectly
                                 // misspelling the function name
                                 // ERROR: compiler will point out
    virtual int Hat() override; // intended to override: incorrectly
                                // misuse the wrong return type
                                // ERROR: compilation error
                                // The return type of virtual function has to
                                // identical of covariant
                                // See C++11 standard Chapter 11
                                // Both C++03 and C++11 will point out
    void Golf(int); // intended to name hiding: correctly  
};
************************************************************

Of course C++11 keeps the back-compatibility to C++03. The program can still be written as usual as C++03. As the limitation/ambiguity caused by C++03's language limitation, C++11 provides a solution/option to let the programmers to choose. And this option will certainly achieve better communication
between the programmers and easier maintainability.

3. Override control - attribute "final" for virtual function
This is the second override control attribute introduced in C++11. What this attribute on virtual function is
trying to achieve is to prevent some virtual function to be overridden in its derived classes.

************************************************************
struct Base {
    virtual void Foo() final;
};

struct Derived : Base {
    void Foo(); // intended to override Base::Foo()
                // ERROR: C++11 does not allow because
                // Base::Foo() declared as "final"
};
***********************************************************

In my opinion this attribute does not make much sense. I see a lot of occasions of forced override but never
see this kind of preventing override. If Base::Foo() is not allowed to be overridden, the best choice is not to declare it as "virtual" at the first place. Personally I think this should be not introduced to the virtual function.
However I prefer the attribute "hiding" for general functions introduced in C++11. This would give more clarity of the programmer's intention to hide some functions from base class rather than understanding as a miss-typing/bug.

Final as an attribute for class make a lot of senses for virtual function.

Bibliography:
[1] http://en.wikipedia.org/wiki/C++11
[2] http://www.stroustrup.com/C++11FAQ.html
[3] http://gcc.gnu.org/projects/cxx0x.html
[4] C++11 Standard
[5] Robert C. Martin, "Clean Code - A Handbook of Agile Sofware Craftsmanship", 2009, Prentice Hall

No comments:

Post a Comment