I don't understand what constructor means in C++ formally. I was reading 3.8 clause (Object lifetime, N3797) and come across with the following:
An object is said to have non-trivial initialization if it is of a
class or aggregate type and it or one of its members is initialized by
a constructor other than a trivial default constructor.
I would like to understand an initialization in general. I've read section 8.5, N3797. Is it true that if some object is initialized, a constructor (possibly trivial default) will be called? I mean that every initialization process (even zero-initialization) means constructor calling. It would be good if you provide corresponding references to the Standard.
I don't understand what contructor means in C++ formally.
As far as I know, the standard does not explicitly contain a definition of the term "constructor". However, §12.1/1 says
Constructors do not have names. A special declarator syntax is used to declare or define the constructor.
The syntax uses:
an optional decl-specifier-seq in which each decl-specifier is either a function-specifier or constexpr,
the constructor's class name, and
a parameter list
in that order. In such a declaration, optional parentheses around the constructor class name are ignored.
Thus, if you declare a member function according to this syntax, the function you are declaring is a constructor. In addition,
The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor
and move assignment operator (12.8), and destructor (12.4) are special member functions. [ Note: The
implementation will implicitly declare these member functions for some class types when the program does
not explicitly declare them. The implementation will implicitly define them if they are odr-used (3.2).
See 12.1, 12.4 and 12.8. — end note ] Programs shall not define implicitly-declared special member functions.
(§12/1)
So there you go---every class has at least three constructors declared, whether implicitly or explicitly; and you can also declare other constructors using the syntax in §12.1/1. The entire set of functions thus declared forms the set of constructors.
Is it true that if some object is initialized, a constructor (possibly trivial default) will be called? I mean that every initialization process (even zero-initialization) means constructor calling.
No, this is false. For example, int has no constructors. This is despite the fact that you can initialize an int with similar syntax compared to initialization of objects of class type.
struct Foo {};
Foo f {}; // calls default constructor
int i {}; // sets the value of i to 0
Also, zero-initialization never invokes a constructor, but zero-initialization is also never the only step in the initialization of an object.
If by "object" you meant "object of class type", it is still not true that a constructor is always called, although three constructors are always declared, as stated above. See §8.5/7 on value initialization:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no accessible default
constructor);
if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is
called.
if T is an array type, then each element is value-initialized;
otherwise, the object is zero-initialized.
Therefore, when the default constructor for a non-union class type is trivial and you are value-initializing an object of that type, the constructor really is not called.
Classes are types:
A class is a type.
§9 [class]
However, not all types are classes. The standard refers to types which are not class types (scalar types, for example) in §3.9.
Only class types, however, can have member functions:
Functions declared in the definition of a class, excluding those declared with a friend specifier, are called member functions of that class.
§9.3 [class.mfct]
Constructors are member functions. Therefore types without constructors can exist (i.e. types that are not class types). Therefore initialization does not necessarily involve calling a constructor, since non-class types (int for example) may be initialized.
Note that something does not have to be of class type to be an "object":
An object is a region of storage.
§1.8 [intro.object]
Therefore an int, while not being of class type, would be an "object".
I think your answer would be found in 3.6.2, and 8.5 of N3797.
A constructor is always called on object creation. But initialization of an object is
multi-step process.
I understand that Zero-Initialization is separate and performed as the first step during object initialization, the Constructor (possibly trivial default) is called later
A constructor is special function. It looks like a normal function. It doesn't have return type (though some say the return type is the object of the class) like void, int, char, double, etc. It has same name as name of Class. It runs automatically when object is created.
In C++, you don't need new operator to initialize an object. Just declare it and set its attribute. i.e. ClassA object1, object2;
For example
Class Player{
int jerseyNo;
//constructor
Player(){
cout<<"HELLO";
}
};
In main you can do the following:
Player nabin;
And you can declare destructor as well. Destructor is special function similar to constructor but runs when object is destroyed i.e. when object moves out of scope.
Example:
Class Player{
..
~Player(){
cout<<"Destructor ran";
}
..
};
P.S The order of execution of constructor and destructor is reverse.
Example:
Player p1,p2,p3;
The order of their execution
1. p1's constructor runs
2. p2's constructor runs
3. p3's constructor runs
4. p3's destructor runs
5. p2's destructor runs
6. p1's destructor runs
You asked:
Is it true that if some object is initialized, a constructor (possibly trivial default) will be called?
The short answer: No.
A longer answer: A constructor is called only for objects of class types. For objects of other types, there are no constructors, and hence constructors cannot be called.
You said:
I mean that every initialization process (even zero-initialization) means constructor calling.
Objects of class types can be initialized by calling constructors, which can be explicit or implicit. They can also be initialized by other initialization methods. Objects of other types are initialized by directly setting the initial values of memory occupied by them.
You have already the seen section 8.5 of the draft standard on initialization.
Details about class constructors can be found in section 12.1 Constructors.
Details about class initialization can be found in section 12.6 Initialization.
Basically, whenever you construct a data type in c++, it can happen in one of two ways. You can either be calling a constructor, or you can essentially be copying chunks of memory around. So constructors are not always called.
In C++, there is a notion of primitives. Integers, doubles, pointers, characters, pointers (to anything) are all primitives. Primitives are data types, but they are not classes. All primitives are safe to copy bitwise. When you create or assign primitives, no constructor is called. All that happens is that some assembly code is generated that copies around some bits.
With classes it's a little more complicated; the answer is that usually a constructor is called, but not always. In particular, C++11 has the concept of trivial classes. Trivial classes are classes that satisfy several conditions:
They use the defaults for all the 'special' functions: constructor, destructor, move, copy, assignment.
They don't have virtual functions.
All non static members of the class are trivial classes or primitives.
As far as C++11 is concerned, objects of any class that satisfy this requirement can be treated like chunks of data. When you create such an object, it's constructor will not be called. If you create such an object on the stack (without new), then its destructor will not be called at program termination either, as would normally be called.
Don't take my word for it though. Check out the assembly generated for the code I wrote. You can see that there are two classes, one is trivial and one is not. The non-trivial class has a call to its constructor in the assembly, the trivial one does not.
Related
In C++, you can declare many things as constexpr: variables, functions (including member functions and operators), constructors, and since C++1z, also if statements and lambda expressions. However, declaring a destructor constexpr results in an error:
struct X {
constexpr ~X() = default; // error: a destructor cannot be 'constexpr'
};
My questions:
Why can't a destructor be marked constexpr?
If I do not provide a destructor, is the implicitly generated destructor constexpr?
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
As per the draft basic.types#10 possibly cv-qualified class type that has all of the following properties:
A possibly cv-qualified class type that has all of the following properties:
(10.5.1) - it has a trivial destructor,
(10.5.2) - it is either a closure type, an aggregate type, or has at
least one constexpr constructor or constructor template (possibly
inherited from a base class) that is not a copy or move constructor,
(10.5.3) - if it is a union, at least one of its non-static data
members is of non-volatile literal type
(10.5.4) - if it is not
a union, all of its non-static data members and base classes are of
non-volatile literal types.
Ques 1: Why a destructor cannot be marked as constexpr?
Because only trivial destructors are qualified for constexpr
Following is the relevant section of the draft
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial
destructors, and
(5.6) — for all of the non-static data members of its class that are
of class type (or array thereof), each such class has a trivial
destructor.
Otherwise, the destructor is non-trivial.
Ques 2: If I do not provide a destructor, is the implicitly generated destructor constexpr?
Yes, because implicitly generated destructor is trivial type, so it is qualified for constexpr
Ques 3: If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
Indeed, this destructor is user-declared and implicitly-generated and thus it is qualified for constexpr.
I'm not able to find any direct reference that only trivial destructors are qualified for constexpr but if the destructor is not trivial then it is for sure that class type is not cv-qualified. So it kind of implicit as you can't define a destructor for cv-qualified class.
C++20 Update
Since C++20, user defined destructors can also be constexpr under certain conditions.
dcl.constexpr/3:
The definition of a constexpr function shall satisfy the following
requirements:
its return type (if any) shall be a literal type;
each of its parameter types shall be a literal type;
it shall not be a coroutine ([dcl.fct.def.coroutine]);
if the function is a constructor or destructor, its class shall not have any
virtual base classes;
its function-body shall not enclose ([stmt.pre])
a goto statement,
an identifier label ([stmt.label]),
a definition of a variable of non-literal type or of static or thread
storage duration.
If what you're looking for is reasoning behind the restriction, have a look at this paper which clearly states that the restriction is artificial - there is no intrinsic property of destructors that prevent them from working in constexpr contexts, and indeed compiler implementors agree that supporting them in constexpr contexts will be trivial to implement.
I guess the C++ standards committee originally placed the restriction in C++11 because they didn't want to deal with destructors at that time and it was easier to just rule them out entirely.
Since C++20, a constructor may be marked constexpr; I don’t know if it says anywhere specifically “a destructor may be constexpr”, but the draft standard includes the following text in section 9.2.5 paragraph 5:
The definition of a constexpr destructor whose function-body is not = delete shall additionally satisfy the
following requirement:
for every subobject of class type or (possibly multi-dimensional) array thereof, that class type shall
have a constexpr destructor.
This also now has a useful function because C++20 also allows new and delete in constexpr contexts, allowing things like vector and string to work at compile time without hacks (although I believe C++20 does not actually include changes to the standard library to allow for this, it is possible to implement something with the same API and behaviour as vector that works completely at compile time).
Why a destructor cannot be marked as constexpr?
The C++11 standard is specific about use of constexpr for consructors and non-static member function. It does not say anything specific about destructor. One may assume that destructors are to be treated as non-static member functions.
constexpr can be used only for const member functions. Since a destructor cannot be const member function, it cannot be qualified as a constexpr member function.
If I do not provide a destructor, is the implicitly generated destructor constexpr.
Since use of
constexpr ~X() = default;
is an error, it makes sense to me that the compiler generated destructor is not a constexpr function. I can't find anything in the standard to justify my statement. I am guessing.
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr
I think not. Once again, I can't find anything in the standard to justify my statement. I am guessing.
FWIW, g++ compiles and builds the following program just fine.
struct X {
constexpr X(int i) : i_(i) {}
~X() = default;
int i_;
};
int main()
{
const X x(10);
}
Reference say's:
constexpr destructors
In most cases, in order to create an object of a type T in a constant
expression, the destruction of T must be trivial. However, non-trivial
destructors are an important component of modern C++, partly due to
widespread usage of the RAII idiom, which is also applicable in
constexpr evaluations. Non-trivial destructors could be supported in
constant expressions, as follows:
Allow destructors to be marked as constexpr
Make defaulted destructors constexpr if they only invoke constexpr destructors
For constexpr variables, require that evaluating the destructor is a constant expression (except that the object being destroyed may be
modified in its own destructor
However, no compelling use cases are known for such a feature, and
there would be a non-trivial implementation cost ensuring that
destructors are run at the right times.
A destructor can't be constexpr because constexpr functions can't have side effects and destructors by definition are only useful through side effects. In short, it would be useless to have a destructor that is constexpr.
A object cannot be constexpr if its destructor is non-trivial. A defaulted one, if trivial, will be considered constexpr
Live
From [class.dtor]
Each decl-specifier of the decl-specifier-seq of a destructor declaration (if any) shall be friend, inline, or virtual.
Missing from it, constexpr. So you could just take it as: because the standard says soTM
Looking at the following example. Does the C++ standard guarantee that the value of object.x will be equal to 1 at the end? What if I don't call the destructor object.~Class();?
#include <new>
class Class
{
public:
Class() {}
~Class() {}
int x;
};
int main()
{
Class object;
object.x = 1;
object.~Class();
new (&object) Class();
object.x == ?
Class object_2;
object_2.x = 1;
new (&object_2) Class();
object_2.x == ?
return 0;
}
No.
The object whose x is equal to 1 was destroyed.
You know that, because you're the one who destroyed it.
Your new x is uninitialised and has an unspecified value, which may be 1 in practice due to memory re-use. That's no different than for any other uninitialised value.
Update
Since there seems to be a lot of people throwing about assertions, here are some facts, direct from the standard.
There is no direct statement about this case because it follows from the general rules governing what objects are and what object lifetime means.
Generally, once you've grokked that C++ is an abstraction rather than a direct mapping of bytes, you can understand what's going on here, and why there is no such guarantee as that the OP seeks.
First, some background on object lifetime and destruction:
[C++14: 12.4/5]: A destructor is trivial if it is not user-provided and if:
the destructor is not virtual,
all of the direct base classes of its class have trivial destructors, and
for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Otherwise, the destructor is non-trivial.
[C++14: 3.8]: [..] The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
the storage which the object occupies is reused or released.
[C++14: 3.8/3]: The properties ascribed to objects throughout this International Standard apply for a given object only during its lifetime. [..]
[C++14: 3.8/4]: A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
(Most of this passage is irrelevant for your first case, as it does have an explicit destructor call: your second violates the rules in this paragraph and thus has undefined behaviour; it shall not be discussed further.)
[C++14: 12.4/2]: A destructor is used to destroy objects of its class type. [..]
[C++14: 12.4/11]: [..] Destructors can also be invoked explicitly.
[C++14: 12.4/15]: Once a destructor is invoked for an object, the object no longer exists. [..]
Now, some specifics. What if we were to inspect object.x before the placement-new?
[C++14: 12.7/1]: [..] For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
Wow, okay.
And what if we were to inspect it after the placement-new? i.e. what value does the x in the new object hold? Is it guaranteed, as the question asks, that it'll be 1? Bear in mind that Class's constructor includes no initialiser for x:
[C++14: 5.3.4/17]: A new-expression that creates an object of type T initializes that object as follows:
If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed, the object has indeterminate value.
Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.
[C++14: 8.5/16]: The initialization that occurs in the forms
T x(a);
T x{a};
as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.
[C++14: 8.5/17]: The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined. [..]
If the initializer is (), the object is value-initialized.
[..]
[C++14: 8.5/8]: To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized.
[..]
[C++14: 8.5/7]: To default-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an
ambiguity or in a function that is deleted or inaccessible from the context of the initialization);
if T is an array type, each element is default-initialized;
— otherwise, no initialization is performed.
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
[C++14: 12.6.2/8]: In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then:
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized (8.5).
[C++14: 8.5/12]: If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value.
So, does the standard guarantee that your replacement object's x has value 1? No. It does not.
In practice, why might it not be? Well, any number of reasons. Class's destructor is non-trivial so, per 3.8, the first object's lifetime has ended immediately after you called its destructor.
Technically, the compiler is then free to place an object at that location as long as it's destroyed by the time the placement-new takes effect. There's no reason for it to do so here, but there's nothing prohibiting it; more importantly, a simple { int x = 5; x = 42; } in between the destructor call and the placement-new would be more than entitled to re-use that memory. It's not being used to represent any object at that point!
More realistically, there are implementations (e.g. Microsoft Visual Studio) that, for debug-mode programs, write a recognisable bit-pattern into unused stack memory to aid in diagnosing program faults. There's no reason to think that such an implementation wouldn't hook into destructors in order to do that, and such an implementation would be overwriting your 1 value. There is nothing in the standard prohibiting this whatsoever.
Indeed, if I replace the ? lines in your code with std::cout statements, so that we actually inspect the values, I get a warning about an uninitialised variable and a value 0 in the case where you used a destructor:
main.cpp: In function 'int main()':
main.cpp:19:23: warning: 'object.Class::x' is used uninitialized in this function [-Wuninitialized]
std::cout << object.x << '\n';
^
0
1
Live demo
I'm unsure how much more proof you need that the standard does not guarantee a 1 value here.
As far as I know, you get an implicit default constructor if you do not declare any constructor yourself. As a job interview question I was asked for a situation where you do not declare a constructor but still do not get an implicit default constructor either. So you end up with a class without any constructors. It is supposed to be code that compiles, so the answer is not having a member variable that does itself not provide a default constructor. Any ideas? Searching through stack overflow and various C++ sites did not reveal anything. Also, as a hint the interviewer said it does not have to do with inheritance.
If my reading of the standard is correct, if the default constructor is not used, it won't get implicitly defined.
C++11 12.1.6:
A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) or when it is explicitly defaulted after its first declaration.
Also, a default constructor can be defined as "deleted", C++11 12.1.5:
A
defaulted default constructor for class X is defined as deleted if:
X is a union-like class that has a variant member with a non-trivial default constructor,
any non-static data member with no brace-or-equal-initializer is of reference type,
any non-variant non-static data member of const-qualified type (or array thereof) with no brace-orequal-initializer does not have a user-provided default constructor,
X is a union and all of its variant members are of const-qualified type (or array thereof),
X is a non-union class and all members of any anonymous union member are of const-qualified type
(or array thereof), or
any direct or virtual base class, or non-static data member with no brace-or-equal-initializer, has class
type M (or array thereof) and either M has no default constructor or overload resolution (13.3) as applied
to M’s default constructor results in an ambiguity or in a function that is deleted or inaccessible from
the defaulted default constructor.
For example, it would appear from the above that the following program is well-formed:
struct X {
X(int) {}
};
struct Y {
X x;
};
Here Y does not have an implicitly defined default constructor because it's both not used and defined as deleted.
No, there is no such trick, unless it's a catch in the exact wording of the question.
12.1p5:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted.
The implicitly declared default constructor might be defined as deleted, but it is still a member of the class.
Effective C++, Item 5: "These functions [ctor, dtor, copy ctor, copy assignment operator] are generated only if they are needed [...]"
According to that, if you have a class that has no declared ctors, and you do not create any instances of that class, then your class will have no constructor(s) at all.
A point from n3290 draft §12.1 (Constructors) ¶5:
An implicitly-declared default constructor is an inline public member of
its class. A defaulted default constructor for class X is defined as deleted
if:
X is a union-like class that has a variant member with a non-trivial
default constructor,
any non-static data member with no brace-or-equal-initializer is of
reference type,
any non-variant non-static data member of const-qualified type (or array
thereof) with no brace-or-equal-initializer does not have a user-provided
default constructor,
X is a union and all of its variant members are of const-qualified type
(or array thereof),
X is a non-union class and all members of any anonymous union member are of
const-qualified type (or array thereof),
any direct or virtual base class, or non-static data member with no
brace-or-equal-initializer, has class type M (or array thereof) and either
M has no default constructor or overload resolution (13.3) as applied
to M’s default constructor results in an ambiguity or in a function that is
deleted or inaccessible from the defaulted default constructor, or
any direct or virtual base class or non-static data member has a type with a
destructor that is deleted or inaccessible from the defaulted default constructor
Please explain the defaulted default constructor with some example program.
I think this excerpt from Wikipedia explains this:
Explicitly-defaulted and deleted special member functions
In C++03, the compiler provides, for classes that do not provide for themselves, a default constructor, a copy constructor, a copy assignment operator (operator=), and a destructor. The programmer can override these defaults by defining custom versions. C++ also defines several global operators (such as operator= and operator new) that work on all classes, which the programmer can override.
However, there is very little control over the creation of these defaults. Making a class inherently non-copyable, for example, requires declaring a private copy constructor and copy assignment operator and not defining them. Attempting to use these functions is a violation of the one definition rule. While a diagnostic message is not required,[5] this typically results in a linker error.[citation needed]
In the case of the default constructor, the compiler will not generate a default constructor if a class is defined with any constructors. This is useful in many cases, but it is also useful to be able to have both specialized constructors and the compiler-generated default.
C++11 will allow the explicit defaulting and deleting of these special member functions. For example, the following type explicitly declares that it is using the default constructor:
Code Example:
struct SomeType
{
SomeType() = default; //The default constructor is explicitly stated.
SomeType(OtherType value);
};
Since you seem to be a Standerdese fan(Almost all of your Questions seek explanations on Standard Quotes) this paper here about how standards committe arrive to defining default and deleted functions should be a good read for you:
Defaulted and Deleted Functions
One of the special member functions is "defaulted" if it is declared with the = default; syntax. The line right before the first line you quoted states:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.4).
Therefore, a "defaulted" default constructor is a default constructor (constructor that can be called with no arguments) that is declared with = default. This can be explicitly defined using the = default syntax, or implicitly defined, per the above line.
I wonder if construction like this (initialization list) has well defined EO (evaluation order):
struct MemoryManager
{
Pair* firstPair_;//<-beg
Pair* currentPair_;
Pair* lastPair_;//<-end
MemoryManager():lastPair_(currentPair_ = firstPair_ = nullptr)
{/*e.b.*/}
};
If yes I personally would prefer this way to the more conventional:
MemoryManager():firstPair_(nullptr),
currentPair_(nullptr),
lastPair_(nullptr)
{/*e.b*/}
As John Dibling has remarked, your construct is technically correct for the given concrete example, but it's brittle and it's hard to understand for many programmers.
Brittleness:
Can fail if order of declaration is changed.
Can fail if the init list is changed.
To evaluate such constructs on your own, keep this idea foremost in your mind: code is not about instructing the compiler to do your bidding, it is about communicating your intent to others (and perhaps your later self).
Hence, try to write clear code.
Cheers & hth.,
Yes. As shown in your code, the members will be initialized in the same order they are declared in the struct/class definition (the order of initializers in the constructor definition is irrelevant, at best you will get a warning telling you they are in an incorrect order).
12.6.2 §5: Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
Note that this is only true for variables that are part of the same access specifier, so for instance variables found in a public: specifier may be initialized before or after those found in a private: specifier (the struct counts as a public: specifier, of course).
EDIT: the above paragraph was incorrect, I was thinking of allocation, not initialization:
9.2 §12: Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (class.access.spec).
However, the more conventional way has a reason to exist, namely that if the order of declaration of the variables changes (for instance, due to refactoring), the code will not break silently. People don't readily assume the order of declaration is relevant, unless a warning tells them otherwise.
If you want to do this, then do it the way everybody understands immediately without having to browse the standard:
MemoryManager()
// no initialization here
{
lastPair_ = currentPair_ = firstPair_ = nullptr;
}
However, I don't really see what this buys you over
MemoryManager()
: lastPair_(), currentPair_(), firstPair_()
{}
which does exactly the same in only about half a dozen more characters.
For this specific example, the initialization order for members is irrelevant. The following constructor would have the same behaviour as the one in the question:
MemoryManager():firstPair_(lastPair_ = currentPair_ = nullptr)
{/*e.b.*/}
This is because the members are POD and so are not default initialized by the constructor at all (12.6.2/4 C++ '03):
If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
If the entity is a nonstatic data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non- static data member of a const-qualified type, the entity class shall have a user-declared default constructor.
Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.
For the raw pointer members above, the 'otherwise' bullet applies.
Now, even if the members did have class type, say:
class MbrType {
public:
MbrType();
MbrType(int *);
MbrType(MbrType const &);
MbrType & operator=(MbrType const &);
};
Then, the constructor as you've written it would result in the members having the values that you expect, but a non optimizing compiler would be allowed to implement your constructor as:
MemoryManager()
: firstPair_ () // implicit call to default constructor
, currentPair_ () // implicit call to default constructor
, lastPair_(currentPair_.operator=(firstPair_.operator=(MbrType (nullptr))))
{/*e.b.*/}
Resulting in 6 calls rather than 3.
No, but it doesn't matter. Your code does not depend on the order in which currentPair_ and firstPair_ are zeroed.