3

Consider the next c++ code fragment

1. In an EXE:

Base.hpp

#ifndef _BASE_H_
#define _BASE_H_ 
class Base
{
    public:
    Base(){};
    virtual double Add(double &x, double &y) = 0; 

};
#endif

main.cpp

#include <iostream>
#include "Base.hpp"
#include "DerivedFactory.hpp"
void main()
{
    Base *theBase = DerivedFactory::Create();
    double x = 4.9, y = 3.3,z;
    z = theBase->Add(x, y);//Works when Add is pure virtual function, but how??? 
                           //Linker error when Add is not pure virtual
}

2. In an implicitly linked DLL

Derived.hpp

#ifndef _DERIVED_H_
#define _DERIVED_H_
#include "Base.hpp"
class Derived : public Base
{
    public:
    double Add(double &x, double &y);
};
#endif

DerivedFactory.hpp

#ifndef _DERIVEDFACTORY_H_
#define _DERIVEDFACTORY_H_
#include "Derived.hpp"

class DerivedFactory
{
    public:
    __declspec(dllexport) static  Derived* Create();
};
#endif

Derived.cpp

#include "Derived.hpp"
 double Derived::Add(double &x, double &y)
{
    return x + y;
}

DerivedFactory.cpp

#include "DerivedFactory.hpp"
Derived* DerivedFactory::Create()
{
    Derived* theDerived = new Derived;
    return theDerived;
}
  1. The main question is how does the exe "know" to address the correct implementation of Add from the dll when the only exported function from it is Create()?

  2. Why do I get linker error when Add() is "just" virtual and not pure virtual?

Benny K
  • 1,957
  • 18
  • 33
  • 1
    On a separate note, names that begin with an underscore followed by a capital letter (`_BASE_H_`, `_DERIVED_H_`, `_DERIVED_FACTORY_H_`) and names that contain two consecutive underscores are reserved for use by the implementation. Don't use them in your code. – Pete Becker Jul 12 '17 at 20:15
  • Also, make the Base destructor either virtual, or protected. With non-virtual destructor, calling delete on Base pointer is UB; when protected, calling delete is prevented and you might have a separate destroy method (which is better because DLL and EXE might have different heaps). – EmDroid Jul 12 '17 at 20:19

2 Answers2

3
  1. The DLL will contain the vtable of Derived somewhere in its global data section. Instances of Derived created within the DLL will have their vtable pointer assigned to that address.

  2. If Base's declaration of Add() is not pure virtual, you must provide an definition for it on top of the declaration.

Edit: For an explanation of what the vtable is, please refer to this answer: why do I need virtual table?

From a purely stylistic standpoint, I would also mention that in this type of situation, the factory typically returns instances of the Base class, like so:

DerivedFactory.hpp

#include "Base.hpp"

class DerivedFactory
{
public:
  __declspec(dllexport) static  Base* Create();
};

This way, the encapsulation of Derived stays entirely, and neatly, in the DLL.

2

The virtual table in the Derived is set by the DLL when creating the instance, so when methods called through the Base pointer, the virtual table will already have the correct addresses set in the virtual table (the EXE will use the Derived virtual table coming from the DLL).

Note that the EXE does not import the virtual table from the DLL when linking, because it does not need to (it accesses the virtual table of the DLL's Derived in memory through the Base interface). Therefore it works not only with implicit linkage, but also when the DLL is loaded at runtime (LoadLibrary/GetProcAddress - but for that the factory method is best not part of the factory class, but standalone extern "C" exported function).

See also here for more details about pure virtual interfaces and DLLs: Providing SDK for my C++ Application

EmDroid
  • 5,918
  • 18
  • 18