A C++ Riddle

Started by
7 comments, last by null_pointer 24 years, 5 months ago
//Allocate some memory.
CBaseClass* p = new CDerivedClass;
Advertisement
Nope. Still doesn't work. Thanks! (it's a stumper)

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).]

Tried it in VC 6.0 and it worked fine. Not only that, but the project I've been working on uses DLLs that are built just that way and I havn't had any problems with it.
The problem must have been fixed then. Thanks for testing it!

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.

From what I know _declspec(dll... is still the standard way of doing it in VC 6.
This is (er, was) a known problem and documented in the Microsoft Knowledge Base.

It appears they fixed it in either 5.0 or 6.0.

KB article: Q122675

------------------
-vince


-vince


Thanks, all!

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!)

Please do not reply without trying the included source code!

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
{
public:
// Just dummy methods
CBaseClass() {};
virtual ~CBaseClass() {};
};

class __declspec(dllexport) CDerivedClass
: public CBaseClass
{
public:
// Just dummy methods
CDerivedClass() {};
virtual ~CDerivedClass() {};
};

++ End DLL Source Code ++

++ Program Source Code ++

// TestProgram.cpp

#include

class __declspec(dllimport) CBaseClass
{
public:
// Just dummy methods
CBaseClass();
virtual ~CBaseClass();
};

class __declspec(dllimport) CDerivedClass
: public CBaseClass
{
public:
// Just dummy methods
CDerivedClass();
virtual ~CDerivedClass();
};

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// Allocate some memory.
CDerivedClass* p = new CDerivedClass;

// Delete the memory.
delete p; // crashes here
p = NULL;

// Then just return.
return 0;
}

++ End Program Source Code ++


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!!

Always happy to help, although I'd like to point out that my handle is not Shrinkage. Look closely.

This topic is closed to new replies.

Advertisement