DLLs have their strong points, but one of their weak points deals with memory. In general: If you link to a DLL statically, it will be placed in your process space, and thus share your heap. When you allocate memory, you can free it from the application or the DLL, since both are using the same heap. However, if you are using dynamic linkage, then you aren't so lucky. The DLL will have it's own heap, and hence memory allocated by the DLL must be released by the DLL, not by the application. Trying to release the memory that the DLL allocated in the application can result in a variety of different bugs, most of the time, however, it will just crash with an access violation.
In general, you should stick to the following simple rule: 'He who allocates it, deallocates it.' Basically, if the DLL allocates a chunk of memory, then it should be the one to deallocate it. Of course, the case here is not so clear cut as one would hope. Passing strings, for instance, can result in under the cover allocations and copy operations, along with their associated deallocations. The easiest way to avoid problems is to either pass raw data, such as character pointer, or to pass constant references. This will force the user to make a copy to make changes, thus preventing allocation errors.
Other tricks involve providing custom allocators to your standard library which all allocate from the same heap. This solution, however, is more complex in general than just obeying the simple rule I stated above, but can have certain advantages.
Our first DLL
First of all, this assumes Visual Studio, and that you've created an empty DLL project and added a source file and a header.
//simple.h#ifndef SIMPLE_AA9B8090_F6C7_4a9c_8858_22D4C92B1EC7#define SIMPLE_AA9B8090_F6C7_4a9c_8858_22D4C92B1EC7#ifdef _MSC_VER#ifdef SIMPLEDLL_EXPORTS#define DLLIMP __declspec(dllexport)#else //SIMPLEDLL_EXPORTS#define DLLIMP __declspec(dllimport)#endif //SIMPLEDLL_EXPORTS#else //_MSC_VER#define DLLIMP#endif //_MSC_VER#include struct IStrange { virtual std::string const& GetName() const = 0; virtual void PrintString(std::string const& str) = 0; virtual void Release() = 0;protected: virtual ~IStrange() = 0 {};};extern "C" DLLIMP IStrange* GetStrange();typedef IStrange* (*GetStrangeSignature)();#endif //SIMPLE_AA9B8090_F6C7_4a9c_8858_22D4C92B1EC7
First off, the macros should be pretty self explanatory, SIMPLEDLL_EXPORTS is defined for me by Visual Studio (it's in the build options under C++) and is the name of the project with EXPORTS appended to it. If that is defined, then DLLIMP will be replaced with __declspec(dllexport), which marks a function as being exported. Otherwise it is replaced by __declspec(dllimport), which marks a function as being imported.
The structure is used to define an interface with three pure virtual functions, note the constant referenced strings. You should also note that the destructor is protected. This prevents the user from calling delete on an IStrange pointer. We also export a single function, GetStrange, which returns a pointer to an IStrange interface. We also provide a typedef of the function signature of the GetStrange function, which I'll demonstrate the usage of later.
Next we wish to create a class that implements the IStrange interface.
#ifndef STRANGEIMPL_F86F9C31_5E3B_4f88_82E4_B8EA1F1BAB26#define STRANGEIMPL_F86F9C31_5E3B_4f88_82E4_B8EA1F1BAB26#include "Simple.h"class StrangeImpl : public IStrange {public: StrangeImpl(); ~StrangeImpl(); std::string const& GetName() const; void PrintString(std::string const& str); void Release();private: std::string name;};#endif //STRANGEIMPL_F86F9C31_5E3B_4f88_82E4_B8EA1F1BAB26
As we can see above, the class is just a simple implementation of the base class, with the addition of a constructor and a single private variable. The definition of the methods:
#include "StrangeImpl.h"#include StrangeImpl::StrangeImpl() : name("StrangeImpl") {}StrangeImpl::~StrangeImpl() {}std::string const& StrangeImpl::GetName() const { return name;}void StrangeImpl::PrintString(std::string const& str) { std::cout<}void StrangeImpl::Release() { delete this;}IStrange* GetStrange() { return new StrangeImpl();}
Note the GetStrange method is found here, and that it returns an instance of our derived class. Another thing that you should note is that the GetName method returns a constant reference to the name string, which is initialized in the constructor to the name of our class...for no particular reason.
If you compile and link this, you will end up with two output files, a dll file, and a lib file. The lib is used when you wish to statically link to a DLL. For both dynamic and static linking, you are required to have the DLL file available.
After reading the notes on unshared resources, however, I see there isn't much I should be using them for at this point. Which is probably a good thing - I'd probably end up hurting myself somehow anyway.
EDIT: was browsing my FTP and found that ^. Swapped with previous image.