Initializing Class Member Variables

Problem

You need to initialize member variables that are native types, pointers, or references.

Solution

Use an initializer list to set the initial values for member variables. Example 8-1 shows how you can do this for native types, pointers, and references.

Example 8-1. Initializing class members

#include 

using namespace std;

class Foo {
public:
 Foo( ) : counter_(0), str_(NULL) {}
 Foo(int c, string* p) :
 counter_(c), str_(p) {}
private:
 int counter_;
 string* str_;
};

int main( ) {

 string s = "bar";
 Foo(2, &s);
}

 

Discussion

You should always initialize native variables, especially if they are class member variables. Class variables, on the other hand, should have a constructor defined that will initialize its state properly, so you do not always have to initialize them. Leaving a native variable in an uninitialized state, where it contains garbage, is asking for trouble. But there are a few different ways to do this in C++, which is what this recipe discusses.

The simplest things to initialize are native types. ints, chars, pointers, and so on are easy to deal with. Consider a simple class and its default constructor:

class Foo {
public:
 Foo( ) : counter_(0), str_(NULL) {}
 Foo(int c, string* p) :
 counter_(c), str_(p) {}
private:
 int counter_;
 string* str_;
};

Use an initializer list in the constructor to initialize member variables, and avoid doing so in the body of the constructor. This leaves the body of the constructor for any logic that must occur at construction, and makes the member variables' initialization easy to locate. A minor benefit over just assigning member variables in the constructor body, to be sure, but the benefits of using an initializer list becomes more apparent when you have class or reference member variables, or when you are trying to deal with exceptions effectively.

Members are initialized in the order they are declared in the class declaration, not in the order they are declared in the initializer list.

Consider a class member variable using the same Foo class from Example 8-1:

class Foo {
public:
 Foo( ) : counter_(0), str_(NULL), cls_(0) {}
 Foo(int c, string* p) :
 counter_(c), str_(p), cls_(0) {}
private:
 int counter_;
 string* str_;
 SomeClass cls_;
};

In Foo's default constructor, you don't need to initialize cls_ because its default constructor will be called. But if you need to construct Foo with an argument, you should add the argument to the initializer list as I did earlier instead of assigning it in the body of the constructor. By taking the initializer list route, you avoid an extra step in the construction of cls_ (because if you assign cls_ a value in the constructor body, cls_ is constructed by its default constructor first, then assigned using the assignment operator, versus being constructed once), but you also gain automatic exception handling. If an object is constructed in the initializer list, and that object throws an exception during construction, the runtime environment destroys all other previously constructed objects in the list, and the exception continues to the caller of the constructor. On the other hand, if you assign the argument in the body of the constructor, then you have to handle the exception with a TRy/catch block.

References are more complicated: initialization of reference variables (and const members) actually requires use of the initializer list. According to the standard, a reference must always refer to a single variable, and can never be changed to refer to another variable. At no time can a reference variable not refer to an object. Therefore, for it to be assigned anything meaningful when a class member variable is a reference, it must happen at initialization, i.e., in the initializer list.

The following is not allowed in C++:

int& x;

That is, you cannot declare a reference variable without initializing it. Instead, you must initialize it to refer to some object. For nonmember variables, initialization looks like this:

int a;
int& x = a;

Well, that's all fine, but this creates a problem for classes. Suppose you want to have a member variable in a class that is a reference, like so:

class HasARef {
public:
 int& ref;
};

Most compilers will accept this until you try to create an instance of the class, like this:

HasARef me;

At this point, you'll get an error. Here's the error you get from gcc:

error: structure `me' with uninitialized reference members

Instead, use the initializer list:

class HasARef {
public:
 int &ref;
 HasARef(int &aref) : ref(aref) {}
};

Then, when you're ready to create an instance of the class, you provide a variable that the ref variable will refer to, like so:

int var;
HasARef me(var);

That's how you initialize member variables safely and effectively. In general, use the initializer list when possible and avoid initializing member variables in the body of the constructor unless you have to. Even if you do have to do something to the variable in the body of the constructor, you can at least use the initializer list to set it to a valid initial value, and then update it in the body of the constructor.

See Also

Recipe 9.2

Building C++ Applications

Code Organization

Numbers

Strings and Text

Dates and Times

Managing Data with Containers

Algorithms

Classes

Exceptions and Safety

Streams and Files

Science and Mathematics

Multithreading

Internationalization

XML

Miscellaneous

Index



C++ Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2006
Pages: 241

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net