FIX: C++ 7.0 May Not Call Destructors for All Array Elements
PSS ID Number: Q103718
Article last modified on 12-21-1993

7.00

MS-DOS


----------------------------------------------------------------------
The information in this article applies to:

 - Microsoft C/C++ Compiler for MS-DOS version 7.0
----------------------------------------------------------------------

SYMPTOMS
========

When dynamically allocating an array of objects, the destructors for
each element of the array may not be called if the following
conditions are met:

 - The base class's destructor is virtual.

 - The derived class implements the destructor and constructor in a
   different source file than the declaration of the class.

 - You allocate an array of objects and subsequently deallocate the
   same array of objects using the delete [] syntax.

CAUSE
=====

The compiler is generating incorrect code when the above conditions
are met. The sample program below demonstrates the problem.

RESOLUTION
==========

There are four workarounds:

 - Implement the constructors and destructors in the same source file
   as the declaration--either in the body of the class declaration or
   in same file as the class declaration.

 - Do not declare the base class destructor as virtual.

 - Declare and allocate an array of pointers to these objects instead
   of allocating an array of objects.

 - Explicitly call the destructors yourself, and then manipulate the
   allocated memory block as the underlying call to free would. The
   destructors should be called in reverse order. Note that this
   method relies on structures of memory objects in C++ 7.0. This may
   or may not work in future versions of the compiler. In addition,
   this workaround is not valid for __near objects under windows.

STATUS
=======

Microsoft has confirmed this to be a problem in C/C++ version 7.0. The
problem was corrected in Microsoft C/C++ version 8.0, which shipped
with Visual C++ for Windows, version 1.0.

Sample Code
-----------

// Compile options needed: /DBAD
//

//----------------------------------------------------------
// TEST1.H
#include<iostream.h>
class MyObj
{
public:
        MyObj() { cout<<"in the ctor of MyObj"<<endl; }
  virtual ~MyObj() { cout<<"in the dtor of MyObj"<<endl; }
};

//----------------------------------------------------------
// TEST2.H
class CTest : public MyObj
{
public:
      CTest();
     ~CTest();
};

//----------------------------------------------------------
// TEST2.CPP
#include "test1.h"
#include "test2.h"
CTest::CTest()
{
   cout<<"in the ctor of CTest"<<endl;
}
CTest::~CTest()
{
   cout<<"in the dtor of CTest"<<endl;
}

//----------------------------------------------------------
// TEST3.CPP
#include "test1.h"
#include "test2.h"
void main(void)
{
     CTest* test = new CTest[20];
#ifdef BAD
     delete [] test;
#endif

// Uncomment this section for Workaround Number Four.
//
// // Check to see if this is a Windows program, and if it is a
// // Small or Medium memory model program.
//
// #if defined(_WINDOWS) && (defined(M_I86MM) || defined(M_I86SM))
// #error Not for near objects under windows.
// #endif
//
//   CTest * p = test;
//   p += *(((unsigned *)test)-1); // The size of the array is
//                       // stored in the word
//                       // preceding the pointer.
//   while( p-- > test )
//     p->CTest::~CTest();
//
//   // This line flips the bit in the malloc header to mark it as free.
//   // It is two words before the pointer.
//
//   (unsigned &)*(((unsigned *)test)-2) |= 1U;
}
//----------------------------------------------------------

Additional reference words: 7.00
KBCategory: Prg
KBSubcategory:

=============================================================================

Copyright Microsoft Corporation 1993.
