Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Managing Objects' Construction Order-3 : Page 3

Dealing with the Construction Order Dependency
Will this application run as expected? Maybe. Everything depends on the order in which the objects admin and lf are constructed. If admin is constructed first, lf's constructor can call admin.getfilename() safely. If, however, your linker chooses the reverse construction order (and it's at liberty to do so!) all hell breaks loose. The call to admin.getfilename() would cause undefined behavior in this case. As a workaround, you can move the definitions of lf and admin into the same translation unit:

#include "admin.h" #include "logfile.h" Admin admin; Logfile lf(admin.getfilename()); int main() { //application code return 0; }

While this tweak ensures that admin is constructed before lf, it's still problematic. The implementer of main.cpp must know the exact order in which the objects should be constructed and declare them accordingly. In real projects, there could be hundreds of objects in dozens of translation units. Grouping all objects in a single translation unit is rarely an option. Besides, future maintainers of main.cpp might mistakenly change the order of definitions, not knowing that by doing so they're wreaking havoc. To conclude, this isn't a plausible solution.

Getting Rid of Construction Order Dependency
Instead of forcing all users to know the precise construction order, you want a mechanism of controlling it. Fortunately, such a mechanism is readily available and is quite simple: replace every global object with a local static object inside an accessor function. For example, instead of the global admin object, use the following accessor function:

//getadmin.cpp #include "admin.h" Admin& getadmin() { //create a local static object when this function is //called for the first time static Admin admin; return admin; }

Similarly, replace the global lf with an accessor function. Notice that in the following function, the construction of lf is guaranteed to succeed because the call to getadmin() always returns a reference to a valid Admin object:

//getlogfile.cpp #include "amdmin.h" #include "logfile.h" Logfile& getlogfile() { static Logfile lf(getadmin().getfilename());//safe return lf; }

This technique neatly solves the order dependency problem. When you call an accessor function for the first time it constructs the local static object and returns a reference to it which you can use safely. This way, you actually determine the construction order of each object! You can call an accessor function from every translation unit. Moreover, subsequent calls return a reference to the same object so you don't need global objects anymore.

Final Refinements
The use of accessor functions solved the construction order dependency but there's still one problem left. How do you ensure that lf is constructed before main() starts? The solution is using a global reference to Logfile that is initialized by the proper accessor function:

#include "getlogfile.h" Logfile & ref=getlogfile(); //initialized before main() int main() { //.. return 0; }

Danny Kalev is a system analyst and software engineer with 13 years of experience, specializing in C++ and object-oriented analysis and design. He is a member of the ANSI C++ standardization committee and the author of ANSI/ISO C++ Professional Programmer's Handbook (Que, 1999, ISBN: 0789720221). Reach him at dannykk@inter.net.il.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date