Overriding Virtual Functions? Use C++0x Attributes to Avoid Bugs.

Overriding Virtual Functions? Use C++0x Attributes to Avoid Bugs.

n July 2009, the C++ standards committee voted into the C++0x Working Paper new attribute tokens that regulate the overriding of virtual functions in a derived class. These new attributes ensure that an overriding function matches its base class counterpart by signature and name.

As trivial as it may seem, overriding a virtual member function can go wrong in many ways, most of which go undetected by the compiler. This 10-Minute Solution shows how to use the new C++0x attributes to avoid a few common bugs associated with overriding virtual functions.

While overriding a virtual function, you accidentally misspell the function’s name or inadvertently changed its parameter list. The compiler doesn’t catch these bugs.

Add the [[base_check]], [[hiding]], and [[override]] attributes to your derived classes. This will help the compiler catch errors that are otherwise undetectable.

Misspelled, Missed Error

Suppose you have a virtual function in a base class B and a derived class D that overrides that function:

struct B { virtual void func();};struct B: D { void func();};

C++ programmers assume that D::func() overrides B::func deliberately, but what if the programmer accidentally picks the name func() without noticing that D::func() overrides B::func()? The compiler can’t catch such accidental misnaming errors because the code is perfectly valid, even though it means something different from what the programmer intended.

Inadvertent overriding is fairly common. It occurs, for example, in projects where class libraries that contain hundreds of virtual functions are used. Two other problems occur even more frequently in class hierarchies:

  • Misspelled function names
  • Parameter mismatches

Let’s look at these two more closely.

Suppose you’re using a library that includes the following class:

struct Base { virtual void some_func1(); virtual void some_func2() const; virtual void some_func3(long);};

You want to derive a new class that will override the three member functions of Base. Unfortunately, you end up with code that does something quite different:

struct Derived: Base { void sone_func1(); //misspelled name, doesn't override void some_func2(); //cv-mismatch, hides Base::some_func2 void some_func3(long long); //parameter mismatch, hides Base::some_func3};

Notice that none of the three member functions of Derived actually overrides the corresponding member function of Base! What went wrong?

The function sone_func1() is misspelled. Instead of overriding Base::some_func1(), the compiler assumes that the programmer simply added a new function to Derived. So, some_func2() hides?rather than overrides?Base::some_func2(), because the two functions have incompatible cv-qualification.

Finally, Derived::some_func3() also hides Base::some_func3() because the parameter lists of the two functions don’t match.

Recall that implicit hiding is valid in C++. Some compilers issue a warning when a member function hides a base class name, while others don’t. The declaration of Derived thus compiles successfully, which makes these bugs much harder to detect, especially when an unsuspecting client uses code written by a third party.

Using Attributes

The compiler can’t guess the programmer’s intent. That is why misspelled function names and accidental hiding of base class functions go undetected. Three new C++0x attributes tackle this problem:

  • [[base_check]]: You use this attribute in a declaration of a derived class. It indicates that any name hiding or virtual function overriding within that class shall be marked with the appropriate attribute. Otherwise, a compilation error will occur.
  • [[override]]: This attribute applies to an overriding member function in a derived class. It indicates that a member function overrides a virtual function in a base class. If no appropriate virtual function is found, the program is ill formed.
  • [[hiding]]: This attribute indicates a member of a derived class that deliberately hides a member with the same name in a base class.

To “activate” the attributes [[override]] and [[hiding]], you first need to declare a derived class with the [[base_check]] attribute. Without [[base_check]], a derived class behaves as usual, allowing you to hide and override base class functions implicitly. A modified version of Derived looks like this:

struct Derived [[base_check]]: Base { void sone_func1 [[override]](); //error, spelling void some_func2 [[override]](); //error, signature void some_func3 [[override]](long long); //ditto};

Because the members of Derived are all declared [[override]], the compiler tries to match each one of them with a corresponding virtual function in the base class but fails. That leads to explicit compilation errors that tell the programmer exactly what’s wrong with the code.

Hide and Seek

You can add new functions to a derived class as well. A new function is indicated by the lack of any attributes:

struct Derived [[base_check]]: Base { int new_func(); //OK//...};

In some cases, hiding a name in a derived class makes sense. For that purpose, you have to use the [[hiding]] attribute:

struct Derived [[base_check]]: Base { void some_func1[[hiding]](double);//OK  void some_func2(short); //error, implicit hiding//...};

Implicit hiding in the class Derived isn’t allowed, because that class is declared [[base_check]]. Omitting [[base_check]] will switch off the compiler’s safeguards, reversing the validity of the functions’ declarations:

struct Derived : Base { void some_func1[[hiding]](double);//error,[[base_check]] required void some_func2(short); //OK, implicit hiding //...};

Attributes Are Your Friends

The attributes proposal drew criticism because some believed it was conceived as a mechanism for sneaking keywords into C++ and because many people don’t like the [[]] syntax. However, attributes are here to stay. They seem to solve a few annoying problems that previously had proved resistant to compilers.

Share the Post:
XDR solutions

The Benefits of Using XDR Solutions

Cybercriminals constantly adapt their strategies, developing newer, more powerful, and intelligent ways to attack your network. Since security professionals must innovate as well, more conventional endpoint detection solutions have evolved

AI is revolutionizing fraud detection

How AI is Revolutionizing Fraud Detection

Artificial intelligence – commonly known as AI – means a form of technology with multiple uses. As a result, it has become extremely valuable to a number of businesses across

AI innovation

Companies Leading AI Innovation in 2023

Artificial intelligence (AI) has been transforming industries and revolutionizing business operations. AI’s potential to enhance efficiency and productivity has become crucial to many businesses. As we move into 2023, several

data fivetran pricing

Fivetran Pricing Explained

One of the biggest trends of the 21st century is the massive surge in analytics. Analytics is the process of utilizing data to drive future decision-making. With so much of

kubernetes logging

Kubernetes Logging: What You Need to Know

Kubernetes from Google is one of the most popular open-source and free container management solutions made to make managing and deploying applications easier. It has a solid architecture that makes

ransomware cyber attack

Why Is Ransomware Such a Major Threat?

One of the most significant cyber threats faced by modern organizations is a ransomware attack. Ransomware attacks have grown in both sophistication and frequency over the past few years, forcing

data dictionary

Tools You Need to Make a Data Dictionary

Data dictionaries are crucial for organizations of all sizes that deal with large amounts of data. they are centralized repositories of all the data in organizations, including metadata such as