[整理]Override and Final CSK in C++11

来源:互联网 发布:mac chm阅读器 编辑:程序博客网 时间:2024/05/22 03:35

CSK,short for content-sensitive keywords,which are different from reserved keywords.C++11 considers them as identifiers that gain special meaning only when used in the specific contexts and locations as I have shown. In any other location or context, they are treated as identifiers. Consequently, the following listing makes perfectly valid C++11 code:

//valid C++11 codeint final=0;bool override=false;if (override==true){ cout<<”override is: “<<override<<endl;}struct D{} final;struct A{virtual bool func(); };struct B:A{ bool func() override final; };


1 Differences betweenoverloading andoverriding   [Extract fromhere]

Overloading generally means that you have two or more functions in the same scope having the same name. The function that better matches the arguments when a call is made wins and is called. Important to note, as opposed to calling a virtual function, is that the function that's called is selected at compile time. It all depends on the static type of the argument. If you have an overload for B and one for D, and the argument is a reference to B, but it really points to a D object, then the overload for B is chosen in C++. That's called static dispatch as opposed to dynamic dispatch. You overload if you want to do the same as another function having the same name, but you want to do that for another argument type. Example:

void print(Foo const& f) {    // print a foo}void print(Bar const& bar) {    // print a bar}

they both print their argument, so they are overloaded. But the first prints a foo, and the second prints a bar. If you have two functions that do different things, it's considered bad style when they have the same name, because that can lead to confusion about what will happen actually when calling the functions. Another usecase for overloading is when you have additional parameters for functions, but they just forward control to other functions:

void print(Foo & f, PrintAttributes b) {     /* ... */ }void print(Foo & f, std::string const& header, bool printBold) {    print(f, PrintAttributes(header, printBold));}


That can be convenient for the caller, if the options that the overloads take are often used.

Overriding is something completely different. It doesn't compete with overloading. It means that if you have a virtual function in abase class, you can writea function with the same signature in thederived class. The function in the derived class overrides the function of the base. Sample:

struct base {    virtual void print() { cout << "base!"; }}struct derived: base {    virtual void print() { cout << "derived!"; }}

Now, if you have an object and call the print member function, the print function of the derived is always called, because it overrides the one of the base. If the function print wasn't virtual, then the function in the derived wouldn't override the base function, but would merely hide it. Overriding can be useful if you have a function that accepts a base class, and every one that's derived from it:

void doit(base &b) {    // and sometimes, we want to print it    b.print();}

Now, even though at compile time the compiler only knows that b is at least base, print of the derived class will be called. That's the point of virtual functions. Without them, the print function of the base would be called, and the one in the derived class wouldn't override it.


2 Override and final CSK   [Source:Use C++11 Inheritance Control Keywords to Prevent Inconsistencies in Class Hierarchies]

For more than 30 years, C++ got along without inheritance control keywords. It wasn’t easy, to say the least. Disabling further derivation of a class was possible but tricky. To prevent users from overriding a virtual function in a derived class you had to lean over backwards. But not any more: Two new context-sensitive keywords make your job a lot easier. Here’s how they work.

C++11 adds two inheritance control keywords: override and final.Override ensures that an overriding virtual function declared in a derived class has the same signature as that of the base class. final blocks further derivation of a class and further overriding of a virtual function. Let’s see how these watchdogs can eliminate design and implementation bugs in your class hierarchies.

Virtual functions and override

A derived class can override a member function that was declared virtual in a base class. This is a fundamental aspect of object-oriented design. However, things can go wrong even with such a trivial operation as overriding a function. Two common bugs related to overriding virtual functions are:

  • Inadvertent overriding.
  • Signature mismatch.

First, let’s analyze the inadvertent overriding syndrome. You might inadvertently override a virtual function simply by declaring a member function that accidentally has the same name and signature as a base class’s virtual member function. Compilers and human readers rarely detect this bug because they usually assume that the new function is meant to override the base class’s function:

struct A{ virtual void func();};             struct B: A{};struct F{};struct D: A, F{  void func();//meant to declare a new function but  //accidentally overrides A::func};

Reading the code listing above, you can’t tell for sure whether the member function D::func() overrides A::func() deliberately. It could be an accidental overriding that occurred because the parameter lists and the names of both functions are identical by chance.

A signature mismatch is a more commonplace scenario. It leads to the accidental creation of a new virtual function (instead of overriding an existing virtual function), as demonstrated in the following example:

struct G{ virtual void func(int);};struct H: G{ virtual void func(double); //accidentally creates a new virtual function};

In this case, the programmer intended to override G::func() in class H. However, because H::func() has a different signature, the result is a new virtual function, not an override of a base class function. Not all compilers issue a warning in such cases, and those that do are sometimes configured to suppress this warning.

In C++11, you can eliminate these two bugs by using the new keywordoverride.Override explicitly states that a function is meant to override a base class’s virtual function. More importantly, it checks for signature mismatches between the base class virtual function and the overriding function in the derived classes. If the signatures don’t match, the compiler issues an error message.

Let’s see how override can eliminate the signature mismatch bug:

struct G{ virtual void func(int);};struct H: G{ virtual void func(double) override; //compilation error};

When the compiler processes the declaration of H::func() it looks for a matching virtual function in a base class. Recall that “matching” in this context means:

  • Identical function names.
  • A virtual specifier in the first base class that declares the function.
  • Identical parameter lists, return types (with one exception), cv qualifications etc., in both the base class’s function and the derived class’s overriding function.

If any of these three conditions isn’t met, you get a compilation error. In our example, the parameter lists of the two functions don’t match: G::func() takes int whereas H::func() takes double. Without the override keyword, the compiler would simply assume that the programmer meant to create a new virtual function in H.

Preventing the inadvertent overriding bug is trickier. In this case, it’s the lack of the keyword override that should raise your suspicion. If the derived class function is truly meant to override a base class function, it should include an explicit override specifier. Otherwise, assume that either D::func() is a new virtual function , or that this may well be a bug.

final Functions and Classes

The C++11 keyword final has two purposes. It prevents inheriting from classes, and it disables the overriding of a virtual function. Let’s look at final classes first.

Certain classes that implement system services, infrastructure utilities, encryption etc., are often meant to be non-subclassable: The implementers don’t want clients to modify those classes by means of deriving new classes from them. Standard Library containers such as std::vector and std::listare another good example of non-subclassable types. These container classes don’t have a virtual destructor or indeed, any virtual member functions.

And yet, every now and then, programmers insist on deriving fromstd::vector without realizing the risks involved. In C++11, non-subclassable types should be declared final like this:

class TaskManager final{/*..*/};  class PrioritizedTaskManager: public TaskManager {};  //compilation error: base class TaskManager is final

In a similar vein, you can disable further overriding of a virtual function by declaring it final. If a derived class attempts to override a final function, the compiler issues an error message:

struct A{  virtual void func() const;};struct B: A{  void func() const override final; //OK};struct C: B{ void func()const; //error, B::func is final};


It doesn’t matter whether C::func() is declared override. Once a virtual function is declared final, derived classes cannot override it.

Syntax and Terminology

I have thus far avoided two side issues pertaining to override and final. The first one is their unique location. Unlike virtual, inline, explicit extern, and similar function specifiers, these two keywords appear after the closing parenthesis of a function’s parameter list, or (in the case of non-subclassable classes) after the class name in a class declaration.

The peculiar location of these keywords is a consequence of another unusual property: override and final aren’t ordinary keywords. In fact officially, they aren’t keywords at all. C++11 considers them as identifiers that gain special meaning only when used in the specific contexts and locations as I have shown. In any other location or context, they are treated as identifiers. Consequently, the following listing makes perfectly valid C++11 code:

//valid C++11 codeint final=0;bool override=false;if (override==true){ cout<<”override is: “<<override<<endl;}struct D{} final;struct A{virtual bool func(); };struct B:A{ bool func() override final; };

It may seem surprising that final and override behave exactly like PL/1’s context sensitive keywords (CSK). Since 1972, C and later C++ always avoided CSK, adhering instead to the reserved keywords approach.

So why did the committee make final and override an exception? The CSK choice was a compromise. Adding override and final as reserved keywords might have caused existing C++ code to break. If the committee had introduced new reserved keywords, they probably would have chosen funky strings such as final_decl or _Override, tokens that were less likely to clash with user-declared identifiers in legacy C++ code. However, no one likes such ugly keywords (ask C users what they think of C99’s _Bool for example). That is why the CSK approach won eventually.

override and final become keywords in C++11, but only when used in specific contexts. Otherwise, they are treated as plain identifiers. The committee was reluctant to call override and final “context sensitive keywords” (which is what they truly are) though. Instead, they are formally referred to as “identifiers with special meaning.” Special indeed!

In Conclusion

The two new context-sensitive keywords override and final give you tighter control over hierarchies of classes, ridding you of some irritating inheritance-related bugs and design gaffes. override guarantees that an overriding virtual function matches its base class counterpart. final blocks further derivation of a class or further overriding of a virtual function. With respect to compiler support, GCC 4.7, Intel’s C++ 12, MSVC 11, and Clang 2.9 support these new keywords.





0 0
原创粉丝点击