For most types, a single call to a copy constructor is more efficient - sometimes much more efficient - than a call to the default constructor followed by a call to the copy assignment operator.
Base classes are initialized before derived classes, and within a class, data members are initialized in the order in which they are declared.Once you’ve taken care of explicitly initializing non-member objects of built-in types and you’ve ensured that your constructors initialize their base classes and data members using the member initialization list, there’s only one more thing to worry about. That’s the order of initialization of non-local static objects defined in different translation units.Let’s first talk about static objects. A static object is one that exists from the time it’s constructed until the end of the program. Stack and heap-based objects are thus excluded. Included are global objects, objects defined at namespace scope, objects declared
static
inside classes, objects declaredstatic
inside functions, and objects declaredstatic
at file scope. Static objects inside functions are known aslocal static objects
(because they are local to a function), and the other kinds of static objects are known asnon-local static objects
. Static objects are destroyed when the program exits, i.e., their destructors are called when main finishes executing. A translation unit is the source code giving rise to a single object file. It’s basically a single source file, plus all of its include files.Now let’s discuss a real problem about initialization of non-local static objects. Imagine at least two separately compiled source files, each of which contains at least one non-local static object (i.e., an object that’s global, at namespace scope, or
static
in a class or at file scope). If initialization of a non-local static object in one translation unit uses a non-local static object in a different translation unit, the object it uses could be uninitialized, because the relative order of initialization of non-local static objects defined in different translation units is undefined.Let’s see an example:
InfileSystem.h
file, you defined1
2
3
4
5
6
7
8
9
10
11class FileSystem
{
public:
...
std::size_t numDisks() const;
...
};
And in `a.cpp` file, you have:
```c++
extern FileSystem theFileSystem; // definition is in some .cpp file in your libraryThen suppose somebody use your code like this in
b.h
andb.cpp
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18////////////////////////////////////////////////
////////////// In b.h file /////////////////////
////////////////////////////////////////////////
class Directory
{
public:
...
Directory();
...
};
////////////////////////////////////////////////
///////////// In b.cpp file ////////////////////
////////////////////////////////////////////////
Directory::Directory()
{
...
std::size_t disks = theFileSystem.numDisks();
}If you’re going to define an object:
1
Directory tempDir;
Unless
theFileSystem
is initialized beforetempDir
,tempDir
‘s constructor will attempt to usetheFileSystem
before it’s been initialized.One solution to this is to make a small design change. All that has to be done is to move each non-local static object into its own function, where it’s declared
static
. These functions return references to the objects they contain. Then call the functions instead of referring to the objects. In other words, non-local static objects are replaced with local static objects. C++ guarantee that local static objects are initialized when the object’s definition is first encountered during a call to that function. Here’s the technique applied to boththeFileSystem
andtempDir
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44////////////////////////////////////////////////
//////////// In fileSystem.h file //////////////
////////////////////////////////////////////////
class FileSystem
{
public:
...
std::size_t numDisks() const;
static FileSystem& theFileSystem();
...
};
////////////////////////////////////////////////
/////////// In fileSystem.cpp file /////////////
////////////////////////////////////////////////
FileSystem& theFileSystem() // this replaces the theFileSystem object
{
static FileSystem fileSystem; // define and initialize a local static object
return fileSystem;
}
////////////////////////////////////////////////
/////////////// In b.h file ////////////////////
////////////////////////////////////////////////
class Directory
{
public:
...
Directory();
static Directory& tempDir();
...
};
////////////////////////////////////////////////
///////////// In b.cpp file ////////////////////
////////////////////////////////////////////////
Directory::Directory()
{
...
std::size_t disks = FileSystem::theFileSystem().numDisks();
...
}
Directory& tempDir() // this replaces the tempDir object
{
static Directory temp; // define and initialize a local static object
return temp;
}The reference-returning functions dictated by this scheme are always simple: define and initialize a local static object on line 1, return it on line 2. This simplicity makes them excellent candidates for inlining, especially if they’re called frequently.