October 30, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Static Constructors in Managed C++

  • March 15, 2004
  • By Kate Gregory
  • Send Email »
  • More Articles »

I have a pet peeve. I hate to read "C++ doesn't have <some feature> but C# does." Or sometimes it's worded "<some feature>, which was always missing from C++, is now in C#." These annoy me because, while they are technically true, they are not usefully true. The feature under discussion may not be part of Standard C++, but if it's part of Managed C++, why go to C# to have it? If you're willing to target the .NET Framework and write a managed application, why not write in Managed C++? And every time I've checked, the feature that C# has "over" C++ is, in fact, available in Managed C++.

Take static constructors, for example. It's easy to find blogs and articles that will tell you C++ doesn't have them. And that's true, Standard C++ doesn't have them. But Managed C++ does. And that's good news for library writers.

What's a Static Constructor?

A static constructor is also called a type constructor. Like the ordinary kind of constructor, it's called "behind-the-scenes" by the compiler, and can't be called by you. But, instead of being called whenever an instance of your class is created (by a stack allocation, a heap allocation with new, or when some temporary objects are created for function parameters or return values), a static constructor is called the first time a static member variable in your class is used. Then, it is never called again. Its purpose is to initialize your static member variables.

Life Without Static Constructors

So, imagine that you write a class library, and it has a class in it like this:

class UsefulThings
{
private:
    static int x;
    static int y;
public:
    static int DetermineXY(int i)
    {
        return i * x * y;
    }
};

(Your code, of course, would be far more interesting, but interesting code tends to drown out language features, so I've spent years now writing methods that add two numbers, reverse strings, and the like. It's an art, really.) This code uses two static variables, x and y, that hold numbers used in internal calculations. Those variables are initialized at declaration, in some file that will only be compiled once (that is, not a header file):

int UsefulThings::x=2;
int UsefulThings::y=6;

If you're not planning to ever change the values of x and y, you could make them const and initialize them right in the class definition, which is convenient:

class UsefulThings
{
private:
    static const int x=2;
    static const int y=2;
public:
    static int DetermineXY(int i)
    {
        return i * x * y;
    }
};

But, what if x and y are not const at all? You might want to initialize them but then go on to change them over the lifetime of your program. Well, in that case, you have to use the "separate declaration in a .cpp file" approach. It feels a little awkward, but you get used to it.

Does anything change if you decide to make UsefulThings a managed class? Absolutely. For one thing, you can initialize those static member variables to literals right in the class definition, like this:

__gc class UsefulThings
{
private:
    static int x=2;
    static int y=6;
public:
    static int DetermineXY(int i)
    {
        return i * x * y;
    }
};

In this version of UsefulThings, x and y are not const and can be changed at will.

Initializing Your Library

Sometimes, the initial values of your static member variables can't be known at compile time. Perhaps you need to read a value from a file, or get it from a database. You want to run some code that initializes the static variable. The tried and true approach is to write a function for this, and teach everyone to call the initializing method before they call any other methods that rely on the static variables:

static bool Initialize()
{
   x = FigureOutX();
   y = x * 3;
   return true;
}

Code that uses this library could look like this:

UsefulThings::Initialize();
int i = UsefulThings::DetermineXY(1);

What happens if someone using your library doesn't remember to call Initialize() before the first call that (directly or indirectly) uses the static member variables on UsefulThings? If you forget to initialize a static member variable, it is initialized to zero. That's not a terribly useful value, but at least you don't get whatever bits of junk were in that memory location from the last time someone used it. I would prefer if you got some sort of exception to remind you that you were making a mistake. You could add a little error checking to the class by having a flag that Initialize() set to 1, and adding code to all the functions that threw an exception if the flag were 0. Keep that in mind if you're writing an unmanaged library. For managed libraries, there's a better way.

Initializing with a Static Constructor

If you think way back to before C++, to programming in C, one of the major annoyances was having to remember to do things. Open the file before you read from it the first time. Allocate memory the first time you work with this struct. Call function1 before you call function2. That was one of the big appeals of C++: you could write a constructor that initialized all the member variables, allocated memory, opened things, and generally took care of making your object ready to use.

If you're writing a class library, you might not want to make your library functions into ordinary member functions and force people to create an instance of the UsefulThings class before they can call library methods. You can see, though, that if you did, there would be a constructor involved, and that constructor could take the place of Initialize(). Lots of class libraries take this approach. You can even leave x and y as static member variables so that, no matter how many instances of UsefulThings are created, they all share the same values for those variables. Those instances will still take up space, and there is a performance cost to create them and clean them up, but many developers were willing to make that tradeoff to get libraries that were self-initializing.

When you make a managed class library, you can get that self-initializing behavior without forcing anyone to create an instance. The secret is a static constructor. The CLR takes care of running the constructor exactly once, before any of the static member variables are touched and before any instances of the class are created. A static constructor looks just like a regular constructor with the word static thrown in:

__gc class UsefulThings
{
private:
    static int x;
    static int y;
public:
    static UsefulThings()
    {
        x = FigureOutX();
        y = x * 3;
    }
    static int FigureOutX();
    static int DetermineXY(int i)
    {
        return i * x * y;
    }
};
Note: If you leave off the __gc, you'll get compiler errors: this is Managed C++ only, even though it's using ordinary-looking keywords.

Now, the calling code can be blissfully unaware of any need to initialize anything:

int _tmain()
{
   Console::WriteLine(__box(UsefulThings::DetermineXY(1)));
   return 0;
}

This is very convenient and elegant. The library takes care of itself and the calling programmer doesn't have to remember anything in order to use it properly. It fits nicely with the spirit of C++. If you're writing a managed library, and you were planning on having an initializing function, why not use a static constructor instead?

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.







Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel