Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > February 1997
February 1997


Code for this article: C++0297.exe (11KB)
Paul DiLascia is a freelance software consultant specializing in training and software development in C++ and Windows. He is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992).

Q I'm using C++ to build a Web application. My app has several static global objects that various parts of the code use. Recently, we did some performance-tuning on our app and discovered that it takes a long time to start up because these global objects (actually, only three of them are significant) take a long time to initialize themselves in their constructors. I don't really need to initialize the objects until/unless they're used—that is, when someone calls one of the member functions to actually do something. If I could defer the initialization, then the app will start up quicker. I could always move all initialization code from the constructor into some other function like CFoo::InitializeObject, and then everywhere do a check to initialize the object. For example;

 CFoo::SomeFunction()
 {
     if (!m_bInitialized) {
         InitializeObject();
         m_bInitialized = TRUE;
     }
 }
But this seems dorky and there are too many functions I would have to change, some of them inline. Is there some easy way I can use C++ to only initialize the object when it's first used?
Jeff Kirpal

A Yes, there is. But first, let me review the problem briefly just to make sure we're all on the same page. In C++, there are three ways you can create an object. You can do it on the stack:


 void MyFunc()
 {
     CFoo foo; // create CFoo object on stack
     •
     •
     •
 }
You can do it by allocating the object in memory:

 pFoo = new CFoo(); // allocate from heap
And you can create an object as a static global variable:

 CFoo foo; // outside of any function
In each of these cases, C++ must generate a call to your CFoo constructor to properly initialize the object. In the case of stack and heap objects, C++ generates a hidden call to the constructor immediately following the allocation (either stack or heap). What about static objects? Since the object is instantiated outside of any function (outside function scope, as we gurus say), there's no place to call the constructor from. So how do these objects get initialized?
Initialization of C++ static objects is accomplished with the help of the startup code that comes with your C++ compiler. In C/C++, the main entry point of any program is called—what else?—main. In Windows
®, it's WinMain. But while these functions are, from your perspective, the beginning of all creation, a lot goes on before your main or WinMain even gets control. There are libraries to initialize, process information to set up, I/O streams to create, and so on. And there are static objects that require initialization.
As the compiler is compiling your program, whenever it comes across a static object, it adds the object to a list. Actually, the compiler doesn't just add the object, it also adds a pointer to one of its constructors and the arguments you called it with, if any. Before passing control to your main or WinMain function, your compiler-supplied startup code calls a function that zips down the list, calling all the constructors with their proper arguments, thus initializing your global static objects.
So now you know how C++ initializes static globals and why, if any of them have CPU-hog constructors, it can take a while for your app to start, as Jeff discovered. The question is, what can you do about it?
Earlier I said there are three ways to instantiate a C++ object: stack, heap, and global static objects outside function scope. Well, that's not quite the whole story. You can also create a "local static" object inside function scope.

 void MyFunc()
 {
     static CFoo foo; // static, inside fn!
     •
     •
     •
 }
In this case foo is like a normal C local static variable. There's only one instance, which lives in fixed memory (not the stack or heap). It behaves like any other static variable, which is to say its value/state are remembered across multiple invocations of MyFunc. All you do by putting foo inside MyFunc is prevent other functions from accessing it. So much for C. In C++, where objects can have constructors, the obvious question is, when does such an object get initialized? The rules of C++ state that foo shall be initialized the first time control passes through its declaration—which is to say, the first time your code calls MyFunc. Conceptually, it works as though the code was written in C as follows:

 void MyFunc()
 {
     static int isInitialized = 0;
     static CFoo foo; 
     if (!isInitialized) {
         // call constructor
         isInitialized = 1;
     }
     •
     •
     •
 }
Pretty clever, eh? This looks sort of like what Jeff was trying to do with his set-a-flag fix, but not quite. Nevertheless, local static variables are perfect for solving Jeff's problem. How? Let's say you have some class CFoo, and a global static instance, theFoo. All throughout your code in various places, you call theFoo.DoThis and theFoo.DoThat to do stuff. Since theFoo is a global static (outside function scope), the compiler initializes it at startup, which is not what you want. You want the compiler to initialize theFoo the first time your program calls CFoo::DoThis or CFoo::DoThat. Well, all you have to do is write a Get function, and use it instead of theFoo.

 class CFoo {
 public:
     CFoo::CFoo();
     // static fn gets one-and-only CFoo object
     static CFoo& GetTheFoo();
 };
 
 CFoo& CFoo::GetTheFoo()
 {
     static CFoo _theFoo;
     return _theFoo;
 }
Now _theFoo lives inside CFoo::GetTheFoo, which is a static function (not to be confused with a static object) that retrieves it. Instead of writing theFoo.DoThis and theFoo.DoThat to call your global CFoo object, you now write CFoo::GetTheFoo().DoThis() and CFoo::GetTheFoo().DoThat().
"Yuk," I hear you say. "I don't want to write all that, and I don't want to do a massive search-and-replace on all my code!" No problem. You can #define a macro to make the new implementation completely invisible to your old code.

 #define theFoo CFoo::GetTheFoo()
Just recompile your code, and no one will ever know the difference. The first function that calls any CFoo function will now end up calling CFoo::GetTheFoo to get the one-and-only global CFoo object, which is the static object _theFoo living inside the function scope of GetTheFoo. CFoo::GetTheFoo will initialize _theFoo (by calling its constructor) before returning _theFoo as a reference. The second time you code accesses theFoo, the second time it calls CFoo::GetTheFoo, _theFoo is already initialized so CFoo::GetTheFoo just returns it as a reference.
I made GetTheFoo return a reference to CFoo (CFoo&) since the global CFoo object is always "there," and hence there's no point returning a pointer. Or, to put it another way, since there is no circumstance under which it would make sense for GetTheFoo to return NULL, it should return a reference, not a pointer. Got it? Local static variables are just the ticket whenever you have a global object that you only want to initialized when or if it's used. I use them frequently.
One last thing: in case you're wondering if there's any similar thing for destructors, you're out of luck. Just as C++ initializes static objects during its startup processing, before calling main, it destroys them from its shutdown code, after main or WinMain returns control. All static objects are destroyed this way—both global and local.

Q I've seen some applications with status bar panes that appear out-dented instead of the normal pushed-in look. Is there an easy way to do this with MFC?

Gary Greco

A Yes. MFC supports both styles of status bar panes, as controlled by the style flags #defined in afxext.h.


 SBPS_NORMAL     // default (pushed in)
 SBPS_POPOUT     // popped-out
 SBPS_NOBORDERS  // neither in nor out (like prompt)
 SBPS_OWNERDRAW  // you can draw
 SBPS_DISABLED   // greyed text
 SBPS_STRETCH    // stretch to fill (like prompt)
SBPS_STRETCH and SBPS_NOBORDERS are normally used for the first pane, which is the prompt ("Ready" indicator). SBPS_STRETCH tells MFC to use all the space remaining after the other fixed panes have been drawn. SBPS_NOBORDERS tells MFC to draw the pane with no borders—that is, neither pushed in nor popped out, but just blended into the background. As you have already guessed, SBPS_POPOUT tells MFC to draw the panes with the "popped-out" look you want.
How do you change the styles? You can access individual status bar pane styles with CStatusBar::GetPaneStyle and CStatusBar::SetPaneStyle. For example, to get the popped-out look, you could add the following lines inside your CMainFrame::OnCreate function, after you've created the status bar:

 CStatusBar *psb = &m_wndStatusBar;
 UINT uFlags = psb->GetPaneStyle(ID_MY_PANE);
 uFlags |= SBPS_POPOUT;
 psb->SetPaneStyle(ID_MY_PANE, uFlags);
Figure 1 The StatusPlane Menu Options
Figure 1 The StatusPlane Menu Options

You can also do it by using the command update mechanism, the same mechanism MFC uses to let you control the state of menu items and toolbar buttons. In the case of a status bar pane, if you set the checked state, MFC gives the pane the SBPS_POPOUT style and draws the pane with the popped-out look (see Figure 1). This approach is better if you want to change the pane style dynamically. Figure 2 shows a program I wrote that has menu commands to let you change whether the status bar panes are pushed in or popped out. The commands just set a Boolean variable, m_bStatusBarOut, which the command update function for the CAPS, NUM, and Scroll Lock indicators then uses to set whether the indicator pane is checked or not.

Have a question about programming in C or C++? Send it to askpd@pobox.com

From the February 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.

© 2014 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft