Embracing Modern C++ Safely - Excerpt
Learn how to utilize these C++ features and avoid pitfalls:
- override
- depracted atrribute
- extern template
- inline namespace
override
TheoverrideMember-Function Specifier
Decorating a function in a derived class with the contextual keywordoverrideensures that avirtualfunction having a compatible declaration exists in one or more of its base classes.
Description
Thecontextual keyword overridecan be provided at the end of a member-function declaration to ensure that the decorated function is indeedoverridinga correspondingvirtualmember function in a base class, as opposed tohidingit or otherwise inadvertently introducing a distinct function declaration:
structBase {virtual voidf(int);voidg(int);virtual voidh(int) const;virtual voidi(int) = 0; };structDerivedWithoutOverride : Base {voidf();// hides Base::f(int) (likely mistake)voidf(int);// OK, implicitly overrides Base::f(int)voidg();// hides Base::g(int) (likely mistake)voidg(int);// hides Base::g(int) (likely mistake)voidh(int);// hides Base::h(int) const (likely mistake)voidh(int)const;// OK, implicitly overrides Base::h(int) constvoidi(int);// OK, implicitly overrides Base::i(int)};structDerivedWithOverride : Base {voidf()override;// Error, Base::f() not foundvoidf(int)override;// OK, explicitly overrides Base::f(int)voidg()override;// Error, Base::g() not foundvoidg(int)override;// Error, Base::g() is not virtual.voidh(int)override;// Error, Base::h(int) not foundvoidh(int)const override;// OK, explicitly overrides Base::h(int)voidi(int)override;/ /好,显式重写基:我(int)};
Using this feature expresses design intent so that (1) human readers are aware of it and (2) compilers can validate it.
As noted,overrideis a contextual keyword. C++11 introduces keywords that have special meaning only in certain contexts. In this case,overrideis a keyword in the context of a declaration, but not otherwise using it as the identifier for a variable name, for example, is perfectly fine:
intoverride = 1;// OK
Use Cases
Ensuring that a member function of a base class is being overridden
考虑下面的多态er的层次结构ror-category classes, as we might have defined them using C++03:
structErrorCategory {virtual bool等效(constErrorCode& code,intcondition);virtual bool等效(intcode,constErrorCondition& condition); };structAutomotiveErrorCategory : ErrorCategory {virtual bool等效(constErrorCode& code,intcondition);virtual boolequivolent(intcode,constErrorCondition& condition); };
Notice that there is a defect in the last line of the example above:等效has been misspelled. Moreover, the compiler did not catch that error. Clients calling等效onAutomotiveErrorCategorywill incorrectly invoke the base-class function. If the function in the base class happens to be defined, the code might compile and behave unexpectedly at run time. Now, suppose that over time the interface is changed by marking the equivalence-checking functionconstto bring the interface closer to that ofstd::error_category:
structErrorCategory {virtual bool等效(constErrorCode& code,intcondition)const;virtual bool等效(intcode,constErrorCondition& condition)const; };
Without applying the corresponding modification to all classes deriving fromErrorCategory, the semantics of the program change due to the derived classes now hiding the base class’svirtualmember function instead of overriding it. Both errors discussed above would be detected automatically if thevirtualfunctions in all derived classes were decorated withoverride:
structAutomotiveErrorCategory : ErrorCategory {bool等效(constErrorCode& code,intcondition)override;// Error, failed when base class changedboolequivolent(intcode,constErrorCondition& code)override;// Error, failed when first written};
What’s more,overrideserves as a clear indication of the derived-class author’s intent to customize the behavior ofErrorCategory. For any given member function, usingoverridenecessarily renders any use ofvirtualfor that function syntactically and semantically redundant. The only cosmetic reason for retainingvirtualin the presence ofoverridewould be thatvirtualappears to the left of the function declaration, as it always has, instead of all the way to the right, asoverride现在所做的。
Potential Pitfalls
Lack of consistency across a codebase
Relying onoverrideas a means of ensuring that changes to base-class interfaces are propagated across a codebase can prove unreliable if this feature is used inconsistently, i.e., not applied in every circumstance where its use would be appropriate. In particular, altering the signature of avirtualmember function in a base class and then compiling the entire code base will always flag as an error any nonmatching derived-class function whereoverridewas used but might fail even to warn where it is not.
Further Reading
Various relationships amongvirtual,override, andfinal(see Section 3.1.“final” on page 1007) are presented inboccara20.
Scott Meyers advocates the use of theoverridespecifier inmeyers15b, “Item 12: Declare overriding functionsoverride,” pp. 79–85.