QM Bites : Maximising Discoverability of Virtual Methods

QM Bites : Maximising Discoverability of Virtual Methods

By Matthew Wilson

Overload, 24(131):24-25, February 2016


C++11 introduced override as a contextual keyword. Matthew Wilson encourages us to use it.

TL;DR:

Reduce opacity in C++ inheritance hierarchies w override k/w (or comments in C++03/earlier)

Bite:

One of the myriad sources of confusion about C++’s ambiguous syntax is in determining whether or not a virtual function is one prescribed in the type one is currently examining or prescribed from a parent class. Consider the following:

  class MidLevel
   : public TopLevel
  {
    . . .
    virtual void SomeMethod();
  };

There are several possible interpretations:

  1. The virtual method SomeMethod() is defined and implemented only within the class MidLevel (and may be overridden by derived types);?
  2. The virtual method SomeMethod() is defined by the class TopLevel (or one of its ancestors) in which it is pure, so MidLevel::SomeMethod() is (at this level) its one and only definition;?
  3. The virtual method SomeMethod() is defined and implemented by the class TopLevel (or one of its ancestors) so MidLevel::SomeMethod() is an override (which may or may not invoke TopLevel::SomeMethod() in its implementation);?

The only way to know is to examine the definition of the class TopLevel , or the definition of (a) parent class of TopLevel , or the definition of (a) grandparent class of TopLevel , or …

Application of the C++ 11 keyword override is a boon to discoverability, in that it connotes that the method is an override of a method declared/defined by a parent class. Hence, Listing 1 implies either case 2 or 3 above.

class MidLevel
 : public TopLevel
{
  . . .
  virtual void SomeMethod() override;
};
			
Listing 1

With C++-98/03 (or any compiler that does not support C++ 11’s override), an alternative declarative technique is simply to use a comment, as in Listing 2, or object-like pseudo-keyword macro (that resolves to override with compilers that support the keyword, and to nothing with those that do not), as in Listing 3 and Listing 4.

class MidLevel
 : public TopLevel
{
  . . .
  virtual void SomeMethod() /* override */;
};
			
Listing 2
#ifdef
  ACMESOFT_COMPILER_SUPPORTS_override_KEYWORD
# define ACMESOFT_override_K    override
#else
# define ACMESOFT_override_K
#endif
			
Listing 3
class MidLevel
 : public TopLevel
{
  . . .
  virtual void SomeMethod() ACMESOFT_override_K;
};
			
Listing 4

Of course, the virtue of the new keyword is far greater than that of connotation of design intent – it facilitates enforcement of design intent. If Listing 1 is presented to the compiler in case 1, it will be a compiler error, since one cannot override something that does not (previously) exist. That’s great.

Just as importantly, it guards against coding errors and/or design changes, in requiring that the overriding method matches the overridden method. If Listing 1 compiles, but then the method signature (or return type, or cv-qualification, or universality) changes in the parent class – the fragile base class problem – it will be a compile error. That’s great too.

(Obviously, neither the comment-form nor the macro-form do any enforcement with non-C++-11-compatible compilation, but it still is a non-trivial amount of help for the human side of things.)

Prior to the advent of override my practice was to distinguish between case 1 and cases 2/3 by placing the virtual keyword in comments, as in Listing 5.

class MidLevel
 : public TopLevel
{
  . . .
  /* virtual */
  void SomeMethod() ACMESOFT_override_K;
};
			
Listing 5

Should you wish, you may do this too, but since override does a superior job in connoting overriding, you might prefer to elide it completely, as in Listing 6.

class MidLevel
 : public TopLevel
{
  . . .
  void SomeMethod() ACMESOFT_override_K;
};
			
Listing 6

After all this you might be wondering what we do about distinguishing between cases 2 and 3. That’s another story (but if I give you a hint and suggest that case 3 is pretty much a design smell, pertaining to modularity as well as discoverability , you might get there before we visit that subject in this place).





Your Privacy

By clicking "Accept All Cookies" you agree ACCU can store cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

By clicking "Share IP Address" you agree ACCU can forward your IP address to third-party sites to enhance the information presented on the site, and that these sites may store cookies on your device.