Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd behaviour of final on a virtual function

I've encountered a strange case when the final keyword is added to a virtual function declaration, with its definition on a separate .cpp file.
Consider the following example:

IClass.hpp

class IClass //COM-like base interface
{
protected:
    virtual ~IClass(){} //derived classes override this

public:
    virtual void release() final;
};

dllmain.cpp (shared library)

#include "IClass.hpp"
...

void IClass::release()
{
    delete this;
}

...

main.cpp (standalone executable)

//various includes here
...

int main(int argc, char** argv)
{
    /* From "IGameEngine.hpp"
       class IGameEngine : public IClass
       {
       ...
       };
    */
    IGameEngine* engine = factoryGameEngine();
    ...
    engine->release();
    return 0;
}

As it is, GCC 4.9.2 will report an undefined reference to 'IClass::release()'
My goal is to have IClass::release() as non-overridable while having its implementation hidden inside the game engine's shared library.
Any suggestions?

like image 699
bit2shift Avatar asked May 18 '15 23:05

bit2shift


People also ask

Can a virtual function be final?

final specifier (since C++11) Specifies that a virtual function cannot be overridden in a derived class or that a class cannot be derived from.

What are the characteristics of virtual function?

Properties of virtual function Virtual functions are resolved during run time resolved during run time hence it is a dynamic binding. Virtual functions are member functions of a class. Virtual functions are declared with the keyword virtual. Virtual function takes a different functionality in the derived class.

What are the rules for virtual function?

Rules for Virtual Functions Virtual functions cannot be static. A virtual function can be a friend function of another class. Virtual functions should be accessed using pointer or reference of base class type to achieve runtime polymorphism.

What happens when a virtual function is called?

A virtual function is a member function that you expect to be redefined in derived classes. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.


1 Answers

Did some digging regarding GCC's usage of final and it turns out virtual functions marked final get "devirtualized", an optimization step that aims at speeding up virtual calls by using static dispatch, and possibly inlining them.

That explains the linker error, as it tries to link IClass::release() into the executable but fails at finding it locally.

This "devirtualization" behaviour also appears on clang, but unlikely to happen with MSVC++


Partially related suggestion

In case you need a way to release an object through a pointer to its abstract class (or abstract base class):

  • The abstract base class needs a pure-virtual destructor
  • Provide the destructor's default definition outside the class (empty scope)
  • Implement the destructor on all derived classes, as usual

  • And if you're also dealing with shared libraries:

  • Export a pair of Malloc/Free functions from the library
  • Override the non-array new/delete operators and their respective std::nothrow versions on your library's header file
  • Call the above Malloc/Free from the overriden operators

  • Since the interface implementation(s) will reside inside the library, export a factory function for each interface you deem client-constructible.
    Just make sure exceptions aren't propagated through the gap between client and library.

    This way, the client application can use delete on an object allocated by the library's CRT, free of hassle.
    like image 185
    bit2shift Avatar answered Oct 07 '22 13:10

    bit2shift