Searching \ for '[OT]: C++ Coding Question: Constructors not always' in subject line. ()
Make payments with PayPal - it's fast, free and secure! Help us get a faster server
FAQ page: www.piclist.com/techref/language/index.htm?key=c%2B%2B
Search entire site for: 'C++ Coding Question: Constructors not always'.

Exact match. Not showing close matches.
PICList Thread
'[OT]: C++ Coding Question: Constructors not always'
2001\01\31@161150 by Bourdon, Bruce

flavicon
face
****** Preface:
If I have already asked you about this, I apologize - please disregard this
email.

A workaround is in place, so there is no need for a rapid response. But I
would appreciate your thoughts eventually, if you have something to say on
this.

****** Environment:
Microsoft Visual C++ 6.0, with the latest service packs. O.S.: Win95.

****** Subject:
C++ Coding question: Shouldn't constructors always be called first, before
the objects member functions?

I have a C++ coding problem, and would be happy to hear from anyone that has
an opinion on it.

****** Overview:
As you may know, the C++ language provides functions that are intended to be
used for object (code and data in memory) initialization and cleanup (i.e.
startup and shutdown),  they are the 'Constructor' and the 'Destructor'
respectively.

Now the weird part: Sometimes member functions are called before the objects
constructor, and again after the objects destructor have been called!

In the attached demo code this does not result in a fatal bug, but in real
code this "out of order" sequence can not be tolerated.

I have a workaround, but it is unsatisfactory. I'd like to hear from anyone
that has a better idea.

****** Attachments:
I pasted the relevant portions of a project to demonstrate this problem
below. Please note that the problem will not occur if all code is place in
one big file (and no, this is not a viable alternative). Also note that this
is a  minimal - non-serious example, intended only to illustrate the
problems behavior.

****** Details:
In the real project there are many modules (files), and resources (memory,
etc) are acquired in many sections.

To keep track of the resources (and to prevent leaks, etc.) I wrote a
resource manager class that would be responsible for supplying, freeing and
keeping track of all resources used throughout program execution.

Obviously, the (single) object instantiated from this class needs to be
created before any resources are required and must exist until all resource
users have exited.

To achieve this, I made a single global instance of this class, available to
all modules.

This appeared to work well until recently.

I discovered that when static objects belonging to other objects (static or
non-static) have code that requires resources in their initialization blocks
(constructors), the previously mentioned resource manager gets called - but
its' member functions may execute BEFORE its' constructor!

Results from running the attached code:

When global object is declared in dep.cpp, function sequence is normal:
In CResMgr::CResMgr()...
In CResMgr::ObtainResource...
In CResMgr::ReleaseResource...
In CResMgr::FindResource...
In CResMgr::~CResMgr...

When global object is declared in rmgr.cpp, function sequence is Incorrect:
In CResMgr::ObtainResource...
In CResMgr::CResMgr()...
In CResMgr::~CResMgr...
In CResMgr::ReleaseResource...
In CResMgr::FindResource...

This is because MS-VC++ seems to initialize code in the order of the file
names they are contained within - with code in files that are named
alphabetically first initialized first.

The obvious workaround is to place the code that must be initialized first
in a file that is named appropriately (this is the current method).

And yes, this does work. But it is a serious hole with the potential to
cause someone significant grief down the road should they be charged with
its' maintenance (if they are unaware of this potential bug…).

You may think that eliminating the global object, by having a static member
object shared by pointers to all code that needs to access it would work. It
does not.

Static objects have their constructors called in the same sequence: the
containing file names alphabetically...

I have also tried creating flags, and verifying the value of the flag to
determine the correct action (i.e. create and initialize the global or
static object if the flag is not the expected value). But this does not work
as the code that assigns the initial values to these flags may or may not
have executed - likely not if the containing filename is late in the
alphabet!

In the real code, I am trying to provide support a large volume of existing
code (some c++ and some c). For example, the resource manager overloads the
new and delete functions. Therefore simply adding separate initialization
functions and mandating they be called before resources may be obtained is
not a viable alternative. Neither is some kind of inheritance scheme, where
all the objects that need to access the resource manager object would
inherit some form of knowledge about it, etc...

It also skirts the central issue: why does MS-VC++ allow an objects' member
functions to be called before its' constructor?

Please see demo code below.
Thanks in advance.
Bruce.


==========================================
File: main.cpp

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <windows.h>
#include "rmgr.h"
#include "dep.h"

///////////////////////////////////////////////////////////////
int main()
{
       class_two c2;

       c2.set_value(1);
       printf(c2.get_value_string());
     
       // done:
   printf("\n\n (press <Enter> to exit)");
   getchar();

       return 0;
}

==========================================
File: dep.cpp

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <windows.h>
#include "rmgr.h"
#include "dep.h"

///////////////////////////////////////////////////////////////
// globals:
//CResMgr g_ResMgr; // <- if done here, all is well...

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// class_one implementation:

///////////////////////////////////////////////////////////////
class_one::class_one(void)
: m_pszMsg(0)
{
       if (g_ResMgr.ObtainResource(&m_pszMsg))
               strcpy(m_pszMsg, "Un-Used");
}

///////////////////////////////////////////////////////////////
class_one::~class_one()
{
       g_ResMgr.ReleaseResource(&m_pszMsg);
}

///////////////////////////////////////////////////////////////
void class_one::store_msg(LPSTR szMsg)
{}
///////////////////////////////////////////////////////////////
LPSTR class_one::get_msg(void)
{
       return m_pszMsg;
}
///////////////////////////////////////////////////////////////
void class_one::delete_msg(LPSTR szMsg)
{}


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// class_two implementation:

// static members:
class_one class_two::m_c1;
int class_two::m_nVal;

///////////////////////////////////////////////////////////////
class_two::class_two(void)
{}

///////////////////////////////////////////////////////////////
class_two::~class_two()
{}

///////////////////////////////////////////////////////////////
void class_two::set_value(int nVal)
{
       itoa(nVal, (char*) m_c1.get_msg(), 10);
}

///////////////////////////////////////////////////////////////
LPCSTR class_two::get_value_string(void)
{
       return m_c1.get_msg();
}

==========================================
File: dep.h

#ifndef DEP_H
#define DEP_H

///////////////////////////////////////////////////////////////
class class_one
{
public:
       class_one(void);
       ~class_one();

       void store_msg(LPSTR szMsg);
       LPSTR get_msg(void);
       void delete_msg(LPSTR szMsg);

private:
       LPSTR m_pszMsg;
};

///////////////////////////////////////////////////////////////
class class_two
{
public:
       class_two(void);
       ~class_two();

       void set_value(int nVal);
       LPCSTR get_value_string(void);

private:
       static class_one m_c1;
       static int m_nVal;
};

#endif /* DEP_H */

==========================================
File: rmgr.cpp

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <windows.h>
#include "rmgr.h"

///////////////////////////////////////////////////////////////
// globals:
CResMgr g_ResMgr;  // <- if done here, problem occurs.

///////////////////////////////////////////////////////////////
// class CResMgr:

///////////////////////////////////////////////////////////////
CResMgr::CResMgr(void)
:       m_nResourceCount(0)
{
       OutputDebugString("In CResMgr::CResMgr()...\n");

       // initialize:
       for (int nCntr=0; nCntr < RES_MGR_MAX_ITEMS; ++nCntr)
               {
               m_ResRecord[nCntr].pszSomeResourceThingy = 0;
               m_ResRecord[nCntr].bSomeKindaFlag = 0;
               // ...
               }

}

///////////////////////////////////////////////////////////////
CResMgr::~CResMgr()
{
       OutputDebugString("In CResMgr::~CResMgr...\n");

       // cleanup:
       for (int nCntr=0; nCntr < RES_MGR_MAX_ITEMS; ++nCntr)
               {
               if (m_ResRecord[nCntr].pszSomeResourceThingy)
                       {
                       delete [] m_ResRecord[nCntr].pszSomeResourceThingy;
                       m_ResRecord[nCntr].pszSomeResourceThingy = 0;
                       m_ResRecord[nCntr].bSomeKindaFlag = FALSE;
                       // ...
                       }
               }
}

///////////////////////////////////////////////////////////////
BOOL CResMgr::ObtainResource(LPSTR* ppTextBuffer)
{
       OutputDebugString("In CResMgr::ObtainResource...\n");

       if (RES_MGR_MAX_ITEMS == m_nResourceCount)
               return FALSE; // error: limit reached.

       *ppTextBuffer = new char [RES_MGR_SILLY_RESOURCE_LENGTH];

       if (NULL != *ppTextBuffer)
               {
               m_ResRecord[m_nResourceCount].pszSomeResourceThingy =
*ppTextBuffer;
               ++m_nResourceCount;
               // ...
               return TRUE;
               }

       return FALSE; // error: failed to obtain storage for resource.
}

///////////////////////////////////////////////////////////////
BOOL CResMgr::ReleaseResource(LPSTR* ppTextBuffer)
{
       OutputDebugString("In CResMgr::ReleaseResource...\n");

       int nIndex;

       if (NULL == *ppTextBuffer)
               return FALSE;

       if (FindResource(*ppTextBuffer, &nIndex))
               {
               delete [] m_ResRecord[nIndex].pszSomeResourceThingy;
               m_ResRecord[nIndex].pszSomeResourceThingy = 0;
               --m_nResourceCount;
               return TRUE;
               }

       return FALSE;
}

///////////////////////////////////////////////////////////////
BOOL CResMgr::FindResource(LPSTR pTextBuffer, int* pnIndex)
{
       OutputDebugString("In CResMgr::FindResource...\n");

       if (NULL == pTextBuffer)
               return FALSE;

       for (int nCntr=0; nCntr < RES_MGR_MAX_ITEMS; ++nCntr)
               {
               if (pTextBuffer == m_ResRecord[nCntr].pszSomeResourceThingy)
                       {
                       *pnIndex = nCntr;
                       return TRUE;
                       }
               }

       return FALSE;
}

==========================================
File: rmgr.h

#ifndef RES_MGR_H
#define RES_MGR_H

///////////////////////////////////////////////////////////////
// globals:
extern class CResMgr g_ResMgr;

///////////////////////////////////////////////////////////////
// constants and stuff:
enum    {RES_MGR_MAX_ITEMS = 10};
enum    {RES_MGR_SILLY_RESOURCE_LENGTH = 64};


// example record structure:
typedef struct tag_RESOURCE_RECORD{
       LPSTR pszSomeResourceThingy;
       BOOL bSomeKindaFlag;
       int     nSomeItherVar;
} RESOURCE_RECORD;


///////////////////////////////////////////////////////////////
// class definition:
class CResMgr
{
public:
              CResMgr(void);
       ~CResMgr();

       BOOL ObtainResource(LPSTR* ppTextBuffer);
       BOOL ReleaseResource(LPSTR* ppTextBuffer);

private:
       BOOL FindResource(LPSTR pTextBuffer, int* pnIndex);

       // Using a fixed array, in real
       // code would use linked list:
       RESOURCE_RECORD m_ResRecord[RES_MGR_MAX_ITEMS];
       int m_nResourceCount;
}; /* CResMgr */

#endif /* RES_MGR_H */

--
http://www.piclist.com hint: To leave the PICList
spam_OUTpiclist-unsubscribe-requestTakeThisOuTspammitvma.mit.edu


2001\01\31@174815 by Bob Ammerman

picon face
Bruce,

I jumped off a bit half-cocked in my previous response (tho' what I said
there is indeed true). I confess I didn't even fully read your message
before responding to it because I immediately 'knew' what the problem was
(unspecified order of construction of static global objects across files).

You said:

>You may think that eliminating the global object, by having a static member
>object shared by pointers to all code that needs to access it would work.
It
>does not.

I am not sure what you mean by this.

I still stand by the idea of making all code and data members of the entire
resource manager class 'static'.

If you want to make sure that an initialization routine (pseudo-constructor)
runs first you could do this:

class CResMan {
public:
    static void   DoSomething();
    static void   DoSomethingElse();
private:
   static void     Initialize();
};

void CResMan::Initialize()
{
   static bool first_time = true;
   if (first_time)
   {
       first_time = false;
       .. whatever...
   }
}

void CResMan::DoSomething()
{
   Initialize();
}

etc.

This works because static variables in function scope are always initialized
no later than the first time the function is entered.

Bob Ammerman
RAm Systems
(contract development of high performance, high function, low-level
software)

--
http://www.piclist.com hint: To leave the PICList
.....piclist-unsubscribe-requestKILLspamspam@spam@mitvma.mit.edu


2001\01\31@190552 by Michael F. Maddox

flavicon
face
Actually, the Constructor and Destructor are called automagically - the constructor when the object is instantiated (either explicitly on the stack, or on the heap with the new operator), and the destructor when the object leaves scope or is otherwise destroyed (i.e., freed from the heap).  Essentially, this rule is inviolate, as it is essential to the OOP paradigm, as expressed in C++.

Without really digging in the code, I suggest that this code is actually creating two instances of the g_ResMgr, although you're attempting to create only one, and use it statically across all modules.  The problem is, I suspect your second instance is locally scoped, and is thereby overriding the external global you're really trying to use.  This could PROBABLY be made to work by explicit use of namespaces.

Can you explain exactly why you're attempting to reference an external class within its own definition file?  Look at rmgr.h.  It attempts to use the external global (global scope, external linkage) you're creating within the rmgr.cpp file.   I assume you want to use an external global as your resources manager, and allow all files that need it to access the instance.  If I wanted to do this, I would instantiate the resource manager object elsewhere, then allow the other modules to link to the instance externally.  This is a common enough metaphor.

I'll load this up into a project and see if I can't give you a better answer by tomorrow.

Michael Maddox
Senior Software Engineer
Hayes
Tallahassee, Florida, USA

Eat hard, sleep hard, wear glasses if you need them.



  It would appear to me that the header and implementation files should be free of the global.  You should move the creation of this global to the
At 04:09 PM 1/31/2001 -0500, you wrote:
{Quote hidden}

--
http://www.piclist.com hint: To leave the PICList
.....piclist-unsubscribe-requestKILLspamspam.....mitvma.mit.edu


2001\01\31@205657 by Bob Ammerman

picon face
>Actually, the Constructor and Destructor are called automagically - the
>constructor when the object is instantiated (either explicitly on the
>stack, or on the heap with the new operator), and the destructor when the
>object leaves scope or is otherwise destroyed (i.e., freed from the
>heap).  Essentially, this rule is inviolate, as it is essential to the OOP
>paradigm, as expressed in C++.

When the constructor/destructor are called are a function of the scope and
location of the object.
Global statics are constructed before the code in main() starts and
destructed after main() exits.

Statics in functions are constructed no later than on the first entry to the
function.

Stack based variables are constructed when they come into scope and
destructed when they leave scope.

Heap based variables are constructed at new time and destructed at delete
time.

>Without really digging in the code, I suggest that this code is actually
>creating two instances of the g_ResMgr, although you're attempting to
>create only one, and use it statically across all modules.  The problem is,
>I suspect your second instance is locally scoped, and is thereby overriding
>the external global you're really trying to use.  This could PROBABLY be
>made to work by explicit use of namespaces.

Namespaces have nothing to do with this.
The external reference in the header file allows other modules to access the
single object. In the module that contains both the extern reference and the
definition the compiler knows that they are really the same thing. (really
no different than having the prototype of a function in a header file -- it
works whether or not you are in the module where the function is declared.)

Bob Ammerman
RAm Systems
(contract development of high performance, high function, low-level
software)

--
http://www.piclist.com hint: To leave the PICList
EraseMEpiclist-unsubscribe-requestspam_OUTspamTakeThisOuTmitvma.mit.edu


2001\01\31@221703 by Bob Ammerman

picon face
Also, try looking up "#pragma init_seg" in MSDN.

Bob Ammerman
RAm Systems
(contract development of high performance, high function, low-level
software)

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestspamspam_OUTmitvma.mit.edu



'[OT]: C++ Coding Question: Constructors not always'
2001\02\01@111715 by Bourdon, Bruce
flavicon
face
Thanks to all for their thoughts and suggestions.

The most attractive method (that actually works) was to either use a static
flag or a static object WITHIN THE FUNCTION used to obtain the objects
pointer/reference/or-call-members/etc...

When the flag (or object) is designated as static within the CLASS, then
there IS NO Garentee of initialization order WHEN THESE STATICS ARE IN
DIFFEENT MODULES (files)!!! (I didn't know this until I read the responses!)

But, as stated by others, when the flag (or object) is designated as static
with a function, it is garanteed to be initialized regardless of whether or
not the code (the caller and calle) are spread across modules (files).

Wow!

Thanks again to all.

Bruce.

{Original Message removed}

More... (looser matching)
- Last day of these posts
- In 2001 , 2002 only
- Today
- New search...