[ Team LiB ] |
![]() ![]() |
Gotcha #51: Confusing Initialization and Assignment in ConstructorsIn a constructor, all class members that legally require initialization will be initialized. Not assigned, initialized. Members that require initialization are constants, references, class objects that have constructors, and base class subobjects. (But see Gotcha #81 regarding const and reference data members.) class Thing { public: Thing( int ); }; class Melange : public Thing { public: Melange( int ); private: const int aConst; const int &aRef; Thing aClassObj; }; // . . . Melange::Melange( int ) {} // errors! The compiler will flag four separate errors in the Melange constructor for failure to initialize its base class and its three data members. Any error flagged at compile time isn't too serious, since we can fix it before it negatively affects anyone's life: Melange::Melange( int arg ) : Thing( arg ), aConst( arg ), aRef( aConst ), aClassObj( arg ) {} A more insidious problem occurs when the programmer neglects to perform an initialization, but the resulting code is nevertheless legal: class Person { public: Person( const string &name ); // . . . private: string name_; }; // . . . Person::Person( const string &name ) { name_ = name; } This perfectly legal code increases the code size and nearly doubles the runtime of the Person constructor. The string type has a constructor, so it must be initialized. It also has a default constructor that will be called if no explicit initializer is present. The Person constructor therefore invokes string's default constructor, only to immediately overwrite the result with an assignment. A better implementation would just initialize the string member and be done with it: Person::Person( const string &name ) : name_( name ) {} Generally, prefer initialization in the member initialization list to assignment in the body of the constructor. Of course, we wouldn't want to push this advice beyond its logical limit. Moderation in all things. Consider a constructor for a nonstandard String class:
class String {
public:
String( const char *init = "" );
// . . .
private:
char *s_;
};
// . . .
String::String( const char *init )
: s_(strcpy(new char[strlen(init?init:"")+1],init?init:"") )
{}
This is carrying things a bit far, and a more appropriate constructor would simply assign within its body: String::String( const char *init ) { if( !init ) init = ""; s_ = strcpy( new char[strlen(init)+1], init ); } Two kinds of data members cannot be initialized in the member initialization list: static data members and arrays. Static data members are dealt with in Gotcha #59. The individual elements of array members must be assigned within the body of the constructor. Alternatively, and often preferably, a standard container, such as a vector, may be used in preference to the array member. |
[ Team LiB ] |
![]() ![]() |