As far as I know, this initialization will be complemented before the function body of the constructor.
If the initialization of data members is the part of constructor, then it should be inlined when the contructor is inlined, otherwise on the contrary.
By the way, how about the constructor list? And is the destroy of data member part of deconstructor?
The assumption is wrong. Compilers may partially inline functions. It's even easier to do so for constructors and destructors as you can't take their address anyway.
Still, that's something you won't notice at C++ level, only if you inspect the assembly.
You can consider the member initialization part of the constructor for functional effects and the member destruction part of the destructor. But what is inlined and what is invoked is not guaranteed. This part is depends totally on the compiler you are using.
Related
Is it safe to call non-virtual functions, including the assignment operator, from the body of a constructor after all member variables have been initialized?
Yes - you can call other non-virtual member functions freely. You can call virtual functions if the most derived base class provides an implementation you happen to want.
Indeed, before C++11 let one constructor call another, it wasn't uncommon for several constructors to call a support function to perform shared initialisation.
operator= can be called in these circumstances - the crucial thing is that any clean-up it might attempt before assigning new state will find sane values to operate on - for example, pointers set to nullptr so delete is safe.
Note that any exceptions from other functions you call that are allowed to cause the constructor to exit (i.e. not caught and suppressed) will prevent the object coming into existence - same as for exceptions thrown directly from the constructor function.
Yes, constructor can make call to non-virtual functions.
Make sure all members have been initialized properly before calling assignment operator otherwise object will be in an inconsistent state.
Use the "Virtual Constructor idiom" when you want to call virtual functions from constructor.
The compiler inserts code into the beginning of the constructor that initializes the VPTR. Is this operation influenced by whether the constructor is the default one or it is explicitly declared? Is there any difference between the two?
It will always be inserted, it has to be for polymorphism to work, at least the way compilers typically have it implemented. It will be initialized always, the constructors you declare (or don't declare) are irrelevant.
Or, hey, maybe it won't (although I do not know any compilers that behave this way), all you need to care about is that polymorphism works, and will always work no matter what constructors you have declared.
Could someone please explain what is meant by the following?
You must define a default constructor if your class defines member variables and has no other constructors. Otherwise the compiler will do it for you, badly.
What are they referring to as "badly"?
From the expansion of that link:
"The reason for this is that if you
have no other constructors and do not
define a default constructor, the
compiler will generate one for you.
This compiler generated constructor
may not initialize your object
sensibly."
Might refer to how new T and new T() differ when there is no ctor provided.
It's good to be sure that the object is created in a known state. Primitive variables won't be set to zero by default, so you could end up with subtle bugs that don't always show up. By initializing the member variables to sensible variables, everything is much more predictable.
The only problem with the default constructor is that it initializes only what the compiler thinks must be initialized, and not what you may think needs to be initialized. Basically, that means that it will invoke initializers for objects with default initializers. It won't set pointers or simple types like int to sane values, etc. If that is sufficient, then the default constructor is not 'bad'. When it is insufficient, it is a bug (in your code) that you did not define the necessary default constructor with the correct initialization.
Take the Google style guide with a grain of salt -- or maybe a truckload of salt.
It is true that the compiler-generated default constructor won't necessarily initialize members that are of built-in types in a meaningful fashion. If you want that done, then yes, its failure to do that is bad. OTOH, if you don't want that done, then its doing it could be somewhat bad (wasteful) as well.
Bottom line: there are times to write your own default ctor, but they tend toward the exception, not the rule. Although there are simple rules of thumb to cover a lot of cases in C++ and will prevent a lot of problems, this really isn't one of them -- here you pretty much do need to know what the compiler-generated ctor will do, and what you want different if you're going to write your own.
In Debug build most compilers fill uninitialized space with some magic values, so that debugging is reliable. And providing custom constructor prevents certain POD optimizations.
In fact, it's a guideline just to make sure people does explicitely make the statement of what is an invalid or default state of any object.
That way, no surprise when reading the code, compared to the actual execution.
However, think that it's a company guideline, that is used to make sure everyone does follow the same rules, that's not a you-must-follow-it-because-google-does-it.
In fact, if you manage to make all your member objects being in valid state when default constructed, or force you to set a constructor, then there is no good reason for such a guideline.
If you have any primitive types as member variables (eg. int, float), then the default ctor will not initialize them. member variables that are a class type will have their default ctor's invoked.
Prefer member initializer lists, so your user supplied ctor may be empty:
class Foo {
int bar;
float baz;
Foo(): bar(0), baz(0.0f) { /* empty ctor body */ }
};
It won't set integers to 0 or pointers to null. It will run default constructors on object of types with constructors.
Some people would call it 'not sensible'.
It just seems a too simplified version of the rules of 3, you should either define yourself or leave the compiler version of
the copy constructor
the assignment operator
the destructor
(Note that by defining yourself a copy constructor, the compiler won't define a default constructor).
The default constructor built by the compiler does 'nothing', it will not even zero the memory occupied by the object
C++ has this funky quirk of supporting initializer lists for a ctor, such as:
class Foo
{
public:
Foo(int x) : m_x(x) { }
private:
SomeComplexObjectThatTakesAnIntForConstruction m_x;
}
Makes sense so far. More efficient because the member is only initialized once, rather than being default-constructed, and then operator= assigned a value later.
But I commonly come across programmers who put the ctor in their .cpp file, where I can hardly believe it actually has the intended (efficient) effect of actually using the initializer list correctly:
// Foo.cpp
Foo::Foo(int x) : m_x(x)
{
// complex set of things needed to be done, or perhaps dependency-inducing references here...
}
As I understand things, the above won't necessarily generate a single construction for m_x, because the initializer-list is not visible outside of this translation unit, and will result in construction + assignment, no?
// user.cpp
Foo my_foo(9); // how can the ctor for m_x be effectively inlined here?
Or have I misunderstood how initializer-lists function?
Thanks for your help with this ;)
I have chosen to split the initializer-list and body of the construction into two pieces, such as:
class Foo
{
public:
Foo(int x) : m_x(x) { Initialize(); }
private:
void Initialize(); // defined in our .cpp thus isolating dependencies and creating a common call-point for multiple ctors (if present)
SomeComplexObjectThatTakesAnIntForConstruction m_x;
}
You have misunderstood.
The initializer list doesn't need to be visible from other translation units the same way that the constructor body doesn't need to be visible from other translation units. It affects the code which is generated for the constructor itself, not the code which is generated to call the constructor.
Maybe this will clear up the confusion:
Inlining is one particular optimization. It is not the only type of optimization possible. Modern C++ compilers are capable of performing all sorts of other optimizations (loop unrolling, reordering of statements when they don't affect the program's behavior, etc).
The "short cut" or "efficiency gain" that inlining gives you is the elimination of the need to create a new frame on the call stack. Typically, the code generated for a function call looks something like this, where lines prefixed by -- are part of the called function (assuming the C calling convention).
Push the arguments on to the stack
Push the current code address onto the stack
Jump to the address of the function
-- Move the stack pointer forward to create space for local variables
-- Execute the body of the function
-- Move the stack pointer back to remove the local variables
-- Pop the caller's address from the stack and jump to it
Pop the arguments from the stack
If the function is inlined, this becomes just the first three steps performed by the called function:
-- Move the stack pointer forward to create space for local variables
-- Execute the body of the function
-- Move the stack pointer back to remove the local variables
This optimization relies on the ability of the compiler and/or linker to change where code is generated, not what code is generated.
In contrast, the initializer list affects what code is generated, not where it is generated. The compiler can still generate calls to non-default constructors for member variables whether it is doing it directly at the call site or in a separate section of of the program code that the call will jump to.
Initializer lists work fine when implemented in .cpp files - what makes you believe they wouldn't?
An initializer list is still part of the constructor 'call'. It's just a syntax that formalizes how construction of class members will take place (note for novices - it doesn't direct or influence the order of class member construction, but it allows parameters to be passed to the member's constructors). This makes possible the simple rule that when the first statement after the opening brace is reached, all class members have been through their construction, but it doesn't mean that the initializer list needs to occur before the constructor is called.
To address Mordachai's comment:
Having the init list in the header vs. in the .cpp file would affect the 'inline-ability' of the constructor (or the initialization list, if you're deferring the main work of the constructor to a function call in the inlined ctor). However, that's true of any in-header implementation of a member function vs. an implementation in a .cpp file.
I suspect that for most ctors, performance concerns will be due to resource allocation -if they aren't acquiring a resource, they're probably not going to have perf issues - and that's going to take the same amount of time whether the ctor is inlined or not. Note that this still means that init lists are important, whether they're inlined or not, because they prevent the situation (that you mentioned in your question) where:
a member object is default initialized (possibly acquiring a resource in an expensive operation)
re-initializing the member object (which may result in releasing the resource, then acquiring a new resource)
Since resource acquisition/release is typically expensive (whether that resource is memory, a network connection, opening a file) compared to many other things, this is an important anti-pattern to avoid. However, the performance difference between whether these resource acquisitions are inlined or not is probably not significant in most cases, I'd think.
Of course, there are also correctness issues that are addressed by the initialization list. For example, since const members can't be modified, they must be initialized in an initialization list.
The purpose of the initializer-list is not simply a matter of efficiency.
Aside from the cases where a member must be initialized there because there is no way to do otherwise (references, const-members, class members with no default constructor), it is generally "preferred" in the same way you initialise variables when you first declare them.
There are occasions where it is better to use the constructor body to set variables to their correct values, for example if you have two pointers that will point to objects created with new, and you are scared the second new may throw. In this case you should still "initialize" them - to NULL - then create them in the body, the first one inside an auto_ptr just in case (which you release after the second one works).
The purpose of moving the constructor body into the compilation unit is to hide the implementation detail from the interface. This is generally preferred for maintainability which a lot of time is hugely more important than a minor amount of runtime efficiency that saves microseconds.
I think initializer lists are there not for efficiency, but for semantics.
For one, they are a chance to initialize members before the superclass's constructor gets called, which could call virtual member functions, coming back invisibly into the lower-level class, before the lower-level constructor had finished.
For another, they are a way of guaranteeing that certain fields are initialized to non-garbage values, unlike assignment statements inside the constructor code that swim in a syntax of if-statements, loops, etc, that the compiler can't be sure will get executed.
For another, it lets you declare the class as const while still allowing you to initialize it. (But I'm not positive about than one.)
As I understand the standard, a trivial destructor is one which is implicitly declared and whose class has only base and non-static members with trivial destructors.
Given the recursivity of this definition, it seems to me that the only "recursion-stopping" condition is to find a base or non-static member with a non-implicitly declared destructor (i.e. user declared).
If that's right, that should mean that a trivial destructor is one which "doesn't have to do anything" and hence it will be declared (implicitly) but not defined.
Saying it in another way: is it correct to say that an implicitly defined destructor (that is "it does something") cannot be trivial as per the standard definition?
Sorry for the kind of silly question, but I'd like to clarify things a bit in my head...
No. An implicitly defined, trivial destructor is by definition trivial :) The difference between the declare and define thingy is that in order for the compiler to even see that a destructor is available, there must always a declaration. So if you don't provide one, it will implicitly provide one.
But now, it will also define one, if that is needed (if an object of that class type is destroyed). In any case, it has to do something: It needs to call the destructors of all its members and base classes. A simple example which illustrates the effect of implicitly defining a destructor:
struct a {
private:
~a();
};
struct bug {
// note: can't be destructed
a a_;
};
As soon as you try to create a local object of bug, the compiler will signal an error, because it yields a definition of a destructor for bug, which tries to call the not accessible destructor of a.
Now, i think triviality of destructors/constructors are mostly used to put constraints on your program. Objects having non-trivial versions of them can't be put in unions, for example. On the other side, you can delete an object having incomplete type, provided it has a trivial destructor. Note that if your program can't decide whether or not a trivial destructor was actually defined, the compiler is allowed to omit defining it. That's the so-called as-if rule. The compiler has to behave as-if it's Standard compliant - optimizations do not matter as long as they don't change the meaning of a program.
Your wording is a bit unfortunate. E.g. the recursion of course also ends when you run out of members and base classes. Those wording problems also seem to get you more confused.
Anyway, all implicitly-declared destructors, whether they are trivial or not, are defined if and only if they are used. Used is a specific term here. A destructor of type T is used whenever the lifetime of a T object ends.
Trivial destructors exist because C programmers put structs in unions. This code should remian legal in C++, so the notion of a trivial destructor was invented for C++. All C structs have trivial destructors, when compiled as C++.
Consider these two classes:
class A {
};
class B {
private:
A obj;
};
The destructors of both these classes implicitly defined. Yet, at the same time, both of them are trivial by the standard definition.