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?
-static-libgcc -static-libstdc++ -mwindows
– ThomasIClass::release()
? Being an interface, it's not exported from the dll, only the factory functions are exported. I was hopingfinal
wouldn't mess with the virtual table consistency. – Thomas__declspec(dllexport)
– ShankleIGameEngine* engine
describes it in short form. If you want the long description, see here – Thomasfinal
require that the function can be linked instead of called via dynamic dispatch? – Shanklefinal
. Kind of a short coming. – Thomasfinal
methods do not need to be dispatched dynamically, so the linker will try to call them directly. And that's not possible since it's not exported from the DLL. – Shankle->release()
is guaranteed to be dispatched to one particular method: so the client calls that method directly, bypassing the vtable, and you get a linker error. Without final, the client feels it must use the vtable in how you call it, so it does not access the symbol for the method, no linker error. – Suttlesengine->IClass::release();
in yourmain
. Now with and withoutfinal
you will get linker error. The fact there was no linker error prior tofinal
was in a sense accidental: it was because nobody called a method directly. Withfinal
everyone now knows thatengine->release()
will meanengine->IClass::release()
, and the latter is faster, so they call it. Boom, linker error. Similar things would happen if you inherited externally fromIClass
I suspect. – Suttles