- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
-
Tips and Techniques
- Using the Swap() Algorithm
- Using class stopwatch for Performance Measurements
- Extending <tt><iostream></tt> to Support User-Defined Types
- Using <tt>auto_ptr</tt> To Automate Memory Management
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part II
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part III
- Using <tt>enum</tt>s as Mnemonic Indexes
- Create Objects on Pre-Allocated Memory Using <tt>Placement-new</tt>
- Online Books: <tt>Placement-new</tt>
- Bitwise Operators
- Bitwise Operators II
- Who's <tt>this</tt>?
- A Reference Guide
- The Virtues of Multiple Inheritance
- Interfaces
- Multiple Inheritance: Construction and Destruction Order
- nothrow new
- POD Initialization
- Object Initialization
- <tt>const</tt> Declarations
- The Semantics of <tt>volatile</tt>
- <tt>inline</tt> Functions
- Project Organization Guidelines
- All About <tt>bool</tt>
- <tt>typedef</tt> Declarations
- State of the <tt>union</tt>
- Dynamic Cast Uses
- Integrating C and C++
- <tt>const</tt>-Correctness
- <tt>const</tt>-Correctness: Advanced Issues
- Sprucing Up Legacy Code
- Virtual Constructors
- Naming Names
- Function Calls
- Speaking Standardese (updated)
- Speaking Standardese: the One Definition Rule
- Declarations and Definitions
- More on Declarations and Definitions
- The Most Vexing Parse
- Finally, At Last
- Sound Bytes (Admittedly Off Topic)
- Local Classes
- Complex Arithmetic
- Floating Point Woes
- String Manipulation
- The Object Model
- The Object Model II
- The Object Model III
- Temporary Objects
- Temporary Objects: Advanced Techniques
- Over-Engineering
- Security Enhancements
- Drop the (automatic) Pilot
- Choosing the Right Container
- Choosing the Right Container II
- Choosing the Right Container, Part III
- Arrays and Pointers
- Low-Level File I/O
- Low-Level File I/O Part II
- static Declarations, Part I
- static Declarations, Part II
- <code>static</code> Initialization Order
- Revisiting the Deprecation of File-Scope Static
- Virtual Memory and Memory Mapping
- Cellular Phone Programming Guidelines
- The Handle/Body Idiom
- Whole Program Optimization, Part I
- Whole Program Optimization, Part II
- Manipulating Directories
- Window Dressing
- <code>friend</code> Declarations
- <code>friend</code> Part II: the Interaction of Friendship and Template Classes
- Forcing Object Allocation on Specific Storage Types
- Lazy Evaluation
- Cache and Carry
- Controlling a Container’s Capacity
- Non-Blocking I/O, Part I
- Non-Blocking I/O, Part II
- Using Unions for Automatic Conversion
- Launching a Child Process
- <tt>switch</tt> Statements
- Introducing the "struct Hack"
- Scoped Enumerators
- Doing Statistics with STL
- Fixing the "Unresolved External" Linkage Error
- Understanding Calling Conventions
- Understanding the Empty Base Optimization
- Implementing RPC with the door Library, Part 1
- Implementing RPC with the door Library, Part 2
- Eliminating Two Common Pointer and <tt>sizeof</tt> Bugs
- Command Line Arguments
- Performance Myths Busting
- Tag Names And Types Part I
- Tag Names And Types Part II
- The Infamous goto
- Trimming Strings
- Can Objects Live Forever? Part I
- Can Objects Live Forever? Part II
- Five Ways to Improve Your Functions
- Member Aggregate Initialization
- Five Futile Coding-Style Debates
- The Good Parasite Idiom: An Exercise in OOD
- The Good Parasite Idiom: An Exercise in OOD, Part II
- The Good Parasite Idiom: An Exercise in OOD, Part III
- Ten Techniques to Reduce the Size of Your Classes, Part I
- Ten Techniques to Reduce the Size of Your Classes, Part II: Inheritance Issues
- Ten Techniques to Reduce the Size of Your Classes, Part III
- Ten Techniques to Reduce the Size of Your Classes IV
- Taking the Address of an Object with an Overloaded Operator <tt>&</tt>
- strcpy() -- How and Why Does It "Just Work"?
- Anonymous Structs
- Five Easy Ways to Reduce The Size of your Executables
- Standard Layout Classes and Trivially Copyable Types, Part I
- Standard Layout Classes and Trivially Copyable Types, Part II
- Five Simple Code Sanity Checks
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
- C++0X: The New Face of Standard C++
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
Object Initialization
Last updated Jan 1, 2003.
A few months ago, I explained the rules of POD initialization. This was only one part of the story, as class member initialization rules are quite different. Here, I will discuss these rules and show how to initialize ordinary data members, constants, members of base classes and embedded objects and static data members.
Assignment versus Initialization
Novices and even experienced programmers sometimes confuse between initialization and assignment, which are two different concepts. For example, the following constructor doesn't really initialize the data members of an object; it only assigns values to them:
class Person
{
private:
int age;
string name;
public:
Person(int a, const string & n)
{ //the members age and name are assigned
age=a;
name=n;
}
};
When dealing with built-in types, the performance difference between such assignments and proper initialization (which I will demonstrate momentarily) is negligible. However, when it comes to objects, as is the case with the member name for example, the difference can be rather noticeable. This is so because the object in question is first constructed and only then is it assigned.
Member Initialization Lists
A proper initialization of data member has a special syntactic construct called a member initialization list or mem-init for short:
class Person
{
//..
public:
//member initialization list:
Person(int a, const string & n) : age (a), name (n)
{}
};
Obligatory Initialization
In the previous example, the initialization of Person's members was optional. Yet there are cases in which you must initialize data members: const members, references, and subobjects (i.e., embedded objects and base classes) whose constructors take arguments require a member initialization list. For example, a class that has a data member of type Person can pass an argument to its constructor like this:
class Foo
{
private:
int & r; // reference member
const int MAX; // const member
Person p; //requires arguments
public:
explicit Foo(int& n) : r(n), MAX(100), p (MAX,"John D."){}
};
The member initialization list of Foo first initializes the reference r, then the const member MAX and finally it initializes the embedded object p.
Notice that the member MAX, once initialized, serves as an initializer of another member, p. This is perfectly legal because members are initialized according to the order in which they are declared in a class. Thus, changing the initialization list into:
Foo(int& r) : p (MAX,"John D."), MAX(100), r(n) {}
Doesn't change the initialization order which remains: r, MAX and p. The reason is that the compiler automatically transforms each member initialization list so that its members are initialized according to their declarations. Notice, however, that initializing a member x with a member y that is declared after x will cause undefined behavior. Consider the following class in which the member Person is declared before MAX:
class BadFoo
{
private:
int & r; // reference member
Person p; //requires arguments
const int MAX; // const member
public:
explicit BadFoo(int& n)//MAX used before being initialized
: r(n), MAX(100), p (MAX,"John D.") {}
};
Don't let the member initialization list mislead you. Although it seemingly initializes MAX before p, the compiler actually transforms the list into:
explicit BadFoo(int& n) //actual initialization order:
: r(n), p (MAX,"John D."), MAX(100) {}
Consequently, MAX has an indeterminate (i.e. garbage) value when it is passed as an argument to p's constructor. Some compilers issue a warning in this case; others don't. To be on the safe side, always check your initialization lists.
Class Constants
In earlier stages of C++, an anonymous enum was used as a constant shared by all instances of the same class:
class Heap
{
enum { PAGE_SIZE=2048 };
//...
};
Anonymous enums can still be used as class constants. However, most compilers nowadays support a superior technique that achieves that same goal, namely in-class initialization of const static data members of an integral type:
class Heap
{
static const int PAGE_SIZE=1024;
public:
Heap(int pages){ p=new unsigned char[pages*PAGE_SIZE];}
//...
};
This kind of initialization is allowed only for const static members of integral types. For any other static members, provide an initializer in the definition itself:
class Trigo
{
const static double PI;
//...
};
const double Trigo::PI= 3.14159265358;
Summary
A member initialization list is the primary form of initializing data members of an object. In some cases, initialization is optional. However, const members, references and subobjects whose constructors take arguments necessitate a member initialization list. C++ also defines a special rule for initializing const static members of an integral type. You may initialize such members inside the class body. All other static data members may be initialized only in their definition, outside the class body.
