A C++ Riddle
BTW, that shouldn't matter. When a object is created - that is derived from another class - there are 2 objects actually created. The first object is the base class and the second object is the derived class. (there can be more than one base class object, but not in this example) This is - theoretically, at least - what the memory looks like:
datadatadatadataCBaseClass{...}CDerivedClass{...}datadatadatadata
The thing coordinates the function calls between the base object and the derived object is called a virtual function table. It basically contains an array of pointers to all the virtual functions in both objects.
When you make a CBaseClass* point to a CDerivedClass object, you are actually only moving the pointer to another part of the complete object, just the CBaseClass part. The function table also allows you to call the overridden virtual functions in the derived class through the base class's pointer. For example, if you had the following code:
++ Begin Source ++
class CBaseClass
{
public:
virtual void DoSomething();
};
class CDerivedClass : public CBaseClass
{
public:
virtual void DoSomething();
};
++ End Source ++
and called DoSomething from a CBaseClass* to a CDerivedClass object, the function called would be CDerivedClass: oSomething() and not CBaseClass:
oSomething().
When the delete operator is called, it does the same thing with the destructor (after all, it's just another type of function). Whether you have a CBaseClass* or a CDerivedClass* to a CDerivedClass object, the CDerivedClass::~CDerivedClass virtual destructor still gets called.
So it really wouldn't change anything.
Thanks for the effort though!!!
Also, if you step through the code in the destructor, even past that to the assembly code, the problem lies in the delete operator, when it tries to release the memory from the object. The error is:
++ Begin Error Text ++
Assertion Failed!
Statement:
ASSERT(pHead == pFirstBlock);
++ End Error Text ++
So it's not detecting the derived class object's memory correctly, and only when it has a virtual destructor in the DLL. Why?
[This message has been edited by null_pointer (edited November 12, 1999).]
I think it came about this way (I could be wrong on this):
Microsoft added their own "Microsoft-specific" keyword to the C++ language to keep a step ahead of the competition. But, unfortunately, the new and delete operators are part of the standard C++ run-time library and can't be changed for each compiler. So they couldn't track the memory usage across the DLL border, when virtual destructors were involved AND the dllexport storage class specifier was used to export the classes. This was fixed in the later version of the standard run-time library when the C++ standard added support for the export and import keywords. (Am I right on this?)
Thanks again.
It appears they fixed it in either 5.0 or 6.0.
KB article: Q122675
------------------
-vince
I really appreciated the Knowledge Base article, and especially your time.
Shrinkage:
Thanks for reading my theory! I heard about the export keyword from a faq on:
http://www.research.att.com/~bs/
(He is the designer and implementor of the C++ language!)
I found a bug (or limitation) in C++. For reference, I use MS Visual C++ 4.0 Standard. (If this "problem" has been fixed in a later version, please let me know.)
NOTE: The __declspec(dllexport) and __declspec(dllimport) keywords were (I think) replaced with the "export" and "import" keywords in later versions of C++.
If you create a DLL containing some classes with virtual destructors, and then a program that uses those classes - using new and delete - an ASSERTion error pops up from the delete operator. To illustrate this "problem", I included some (relatively) simple source code:
++ The DLL Source Code ++
// TestDLL.cpp
#include class __declspec(dllexport) CBaseClass class __declspec(dllexport) CDerivedClass ++ End DLL Source Code ++ ++ Program Source Code ++ // TestProgram.cpp #include class __declspec(dllimport) CBaseClass class __declspec(dllimport) CDerivedClass int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) // Delete the memory. // Then just return. ++ End Program Source Code ++
{
public:
// Just dummy methods
CBaseClass() {};
virtual ~CBaseClass() {};
};
: public CBaseClass
{
public:
// Just dummy methods
CDerivedClass() {};
virtual ~CDerivedClass() {};
};
{
public:
// Just dummy methods
CBaseClass();
virtual ~CBaseClass();
};
: public CBaseClass
{
public:
// Just dummy methods
CDerivedClass();
virtual ~CDerivedClass();
};
{
// Allocate some memory.
CDerivedClass* p = new CDerivedClass;
delete p; // crashes here
p = NULL;
return 0;
}
Is this just a bug in the Microsoft compiler? Or is it a problem with DLLs? Thanks for your patience - this has really bugged me!!