Let's say you have a C++ class that you would like to publish to
the rest of the C++ community. What about a string class? There
aren't nearly enough of those about. The class looks like this:
Header file, mystring.h:
class
CMyString { private: char
*m_psz; public: CMyString(const char *
psz); ~CMyString();
const char*Find(const char *psz); int
Length(); };
Source file, mystring.cpp:
CMyString::CMyString(const char *
psz) : m_psz( new char[psz ? strlen(psz)+1 :1])
{ if ( psz
)
strcpy(m_psz,psz);
else m_psz[0] =
0; } CMyString::~CMyString() { delete []
m_psz; } const char*CMyString::Find(const char *psz)
{ return strstr(m_psz,psz); } int
CMyString::Length() { return
strlen(m_psz); }
What is the optimal way of packaging this class for use by the
rest of the C++ world?
One option would be to provide the class in a library that can be
statically linked into any target executable client wishing to use
it. Just to recap, static linking means that any
external references made within the executable client to
symbols that exist within the library must be resolved against
library code or data at build time. The executable client has to be
code and data complete at build time before it can be loaded and
executed.
There are three main disadvantages to doing this. All are due to
the fact that library code and data has to exist in every executable
client that uses it.
- There is no way for an operating system to detect a library's
presence when loading an executable. So there is no way for the
operating system to share the library code with any other
executable statically linking to the same library. Any executable
client that statically links to a library is therefore bigger in
memory than it needs to be. Performance will degrade due to page
faults. As code and data will live on many different duplicate
pages in memory, over time pages must be swapped to disk.
- Any executable client that statically links to a library is
bigger on disk than it needs to be, since the library code has to
be contained in every executable so linked.
- There is no way to upgrade the library code in situ,
since the library code is inextricably bound to any executable
client that statically links to it. This means bug fixes or
functionality upgrades are impossible to achieve in the field.
If you would like to write some code to expose a C++ class from a
static link library and to verify the aforementioned assertions, click here.
Another option would be to provide the class in a library that
can be dynamically linked into any target executable client wishing
to use it. Just to recap, dynamic linking means that any
external references made within the executable client to
symbols that exist within the library can be resolved against
library code or data at loadtime or runtime. The executable client
does not have to be code and data complete at buildtime. It merely
contains references to import records that can be used to fix up the
references to the library at loadtime of the executable, or it
contains no references at all and library code and data can be
programmatically loaded and bound at runtime. However, before the
executable client can be loaded and/or executed, the library must be
successfully located and loaded and all references fixed up. Linkage
is merely deferred.
The class definition has to change somewhat.
Header file, mystring.h:
#ifdef MYSTRINGDLL #define
EXPORTORIMPORT _declspec(dllexport) #else #define
EXPORTORIMPORT _declspec(dllimport) #endif
class EXPORTORIMPORT
CMyString { private: char
*m_psz; public: CMyString(const char *
psz); ~CMyString();
const char*Find(const char *psz); int
Length(); };
As long as the DLL implementation defines the MYSTRINGDLL symbol
as
#define MYSTRINGDLL
before the line
#include "mystring.h"
the class will get exported correctly from the DLL and imported
correctly into the target executable client.
The advantages to dynamic linking mainly counteract the
disadvantages of static linking. All are due to the fact that the
library code and data does not have to exist in every executable
client that uses it. Each executible client merely needs a reference
to the code and data in one single library.
- There is a way for the operating system to detect a library's
presence when loading an executable. So there is a way for the
operating system to know to share the library code with another
executable client dynamically linking to the same library. An
executable client that dynamically links to a library is therefore
no bigger in memory than it needs to be.
- Any executable client that dynamically links to a library is
no bigger on disk than it need be, since the library code is not
contained in every executable client so linked, merely references
to the library code.
- There is a way to upgrade the library code in situ,
since the library code is not inextricably bound to any executable
client that dynamically links to it. References are fixed up at
loadtime or runtime of the executable client. This means bug fixes
or functionality upgrades are possible to achieve in the field.
The caveat here is that the public interface of the DLL exported
functions does not change (in this case, any of the class method
signatures).
- There need not be any reference to the library in the
executable client that makes use of it. The library may be
dynamically loaded and bound at run-time, making dynamic discovery
of code and data possible.
If you would like to write some code to expose a C++ class from a
DLL and to verify the aforementioned assertions, click here.
For a more thorough review of dynamic linking, click
here.
Next
we will look at how using different C++ compilers complicates
matters.

|