9

I apologize in advance because some of my verbiage may not be 100% correct.

I will have a class like this:

class ClassName {
private:
    AnotherClass class2;
public:
  ClassName();
  ~ClassName();
...

In the constructor of this class, among other things, I put the line

ClassName::ClassName() {
    AnotherClass class2; 
}

Which is how I thought you were supposed to initialize objects in C++, however I was noticing (through GDB) that two AnotherClass objects were being created. Once on the Constructor definition then again on my initialization line. What is the reasoning behind this? What if I wanted to use a more complicated constructor like AnotherClass(int a, int b), would it create a temporary object then create the correct one shortly after?

Maeve Kennedy
  • 239
  • 1
  • 4
  • 11
  • 1
    You should use initializer lists http://stackoverflow.com/questions/4589237/in-this-specific-case-is-there-a-difference-between-using-a-member-initializer – SingerOfTheFall Sep 30 '15 at 07:29
  • 1
    You've defined another local variable that happens to share the same name as your class member (`class2`), if you think that this is how class members are initialized I think you might want to pick something from [this](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) list and have a good read. – user657267 Sep 30 '15 at 07:32
  • 1
    @user657267 I infact am, no need to be condescending. C++ is complicated my selected book is 1000 pages. The notion of a container doesn't start til 600 pages for instance. – Maeve Kennedy Sep 30 '15 at 13:46
  • @BradleyKennedy I wasn't trying to be condescending, sorry if it came across that way, I just wanted to point out that you should probably learn *how* to use certain constructs before you actually attempt to use them in code, otherwise you're going to find you have to unlearn things further down the line. – user657267 Sep 30 '15 at 23:57

5 Answers5

7

AnotherClass class2; creates another local object inside the constructor body, that gets destroyed at the end of the body. That is not how class members are initialized.

Class members are initialized before the constructor body in the member initializer list between the constructor signature and body, starting with a :, like so:

ClassName::ClassName() :
    class2(argumentsToPassToClass2Constructor),
    anotherMember(42) // just for example
{
    /* constructor body, usually empty */
}

If you don't want to pass any arguments to the class2 constructor you don't have to put it in the initializer list. Then its default constructor will be called.

If you simply want to call the default constructor on all of your class members, you can (and should) omit the constructor altogether. The implicitly generated default constructor will do just what you wanted.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • 1
    Your answer and Joachim have provided good concise answers. I chose yours strictly due to your comments about not promoting bad programming practices on another answer, which is constructive in nature. – Maeve Kennedy Sep 30 '15 at 14:41
2

What you are doing in your constructor is creating another variable, local only inside the constructor.

Actually, if you do nothing, the default constructor in AnotherClass will be called for the class2 object.

If you want to be explicit, you can use a constructor initializer list:

ClassName::ClassName()
    : class2()
{
}

This last method is also the way you call a specific constructor with arguments in AnotherClass, if you need to do that.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
2
ClassName::ClassName() {
    AnotherClass class2; // this will create local variable only
}

If AnotherClass will have default constructor, then it will be called for the class2 object by compiler.

If you want to call parametrized constructor then you will have do it in following way:

ClassName::ClassName() :
    class2(arguments)

Why to use and How to use initializer list :

Consider the following example:

// Without Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a) {  // Assume that Type is an already
                     // declared class and it has appropriate 
                     // constructors and operators
      variable = a;
    }
};

Here compiler follows following steps to create an object of type MyClass

  1. Type’s constructor is called first for “a”.
  2. The assignment operator of “Type” is called inside body of MyClass() constructor to assign

    variable = a;

  3. And then finally destructor of “Type” is called for “a” since it goes out of scope.

Now consider the same code with MyClass() constructor with Initializer List

// With Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a):variable(a) {   // Assume that Type is an already
                     // declared class and it has appropriate
                     // constructors and operators
    }
};

With the Initializer List, following steps are followed by compiler:

  1. Copy constructor of “Type” class is called to initialize : variable(a). The arguments in initializer list are used to copy construct “variable” directly.
  2. Destructor of “Type” is called for “a” since it goes out of scope.

As we can see from this example if we use assignment inside constructor body there are three function calls: constructor + destructor + one addition assignment operator call. And if we use Initializer List there are only two function calls: copy constructor + destructor call.

This assignment penalty will be much more in “real” applications where there will be many such variables.

Few more scenarios, where you will have to use initializer list only:

  1. Parametrized constructor of base class can only be called using Initializer List.
  2. For initialization of reference members
  3. For initialization of non-static const data members
CreativeMind
  • 897
  • 6
  • 19
0

You are just creating a local variable in this line. In general there are three ways of initializing private members:

  1. Default initialization

If you do nothing on your constructor, the compiler will automatically initialize the private member by calling its default constructor (ctr without parameters)

  1. Assigning them to a value in the ctr body

In this case you have to assign the desired value to your private member by using the assignment operator.

ClassName::ClassName()
{
    class2 = AnotherClass(a, b, c); // if the class ctr has some parameters
}
  1. By using the initialization list

In your case it will be something like:

ClassName::ClassName()
    : class2(initial_value)
{
}

This is in general the best and efficient option for initializing your class private members since you avoid calling the copy constructor for the passed parameters. This is in general is not an issue unless the copy ctr contains time-consuming operations. The same apply for the option #2 in this case you may have the same issues with the assignment operator

rkachach
  • 16,517
  • 6
  • 42
  • 66
  • 2 isn't initialization, its assignment to an already default-constructed object. – Emil Laine Sep 30 '15 at 07:46
  • Yes, but it's among the techniques used to initialize private members. Which is the main question. – rkachach Sep 30 '15 at 07:48
  • Assigning is a pretty bad "technique" for "initializing" stuff. For primitive types such as `int` that are initialized with random junk data it doesn't matter but for more complex types it makes a difference. Please don't spread non-idiomatic programming style. Also your list is not exhaustive, there's [in-class initialization](http://stackoverflow.com/q/13662441/3425536). Also why is the best and most efficient way last inyou answer, wouldn't it make more sense for it to be first? – Emil Laine Sep 30 '15 at 07:53
  • bad or good it depends on the situation. If you are doing "simple" initialization then the initialization list is ok. If the class initialization requires a specific order of constructing variables (because of dependencies, or whatever) then you have no other choice other than using assignments in the ctr body. As you know, the order of the members in the initialization list is not the real order of initializing things. I'm not spreading non-idiomatic styles, I'm just listing the different methods exiting for initialization.It's up to the programmer to choose the one which fulfill his needs. – rkachach Sep 30 '15 at 08:01
  • Member initialization order is the same as the order of the member declarations in the class definition. If a member depends on some other member, then it should be declared after it. Can you give an example of a situation where that doesn't work, and you'd have to resort to default-initialization-then-assignment? I've never come across such a situation, only situations where that is a quick-and-dirty solution instead of the right solution. – Emil Laine Sep 30 '15 at 08:07
  • If you read my answer it says (for the initialization lists): This is in general the best and efficient option for initializing your class. So I think it's clear that my recommendation is to us them whenever is possible. As for your question a simple example is when you are using dynamic memory with raw pointers. In this case if some member throw an exception the destr of the class under construction is not called and there's a risk of leaking memory. – rkachach Sep 30 '15 at 08:57
  • That's not a reason to default-initialize-then-assign, that's a reason not to use raw pointers for dynamic memory allocation: `std::make_unique` and `std::make_shared` are your friends. – Emil Laine Sep 30 '15 at 09:27
0

What you did there is to create a new variable with the same name as you member,
By doing this you overshadowed your member variable.
Also, in the process your member constructor was silently called in the ClassName empty initialisation list.

you can initiate the class in two ways:

    ClassName::ClassName(): class2() {}

or:

    ClassName::ClassName() {
        this->class2 = AnotherClass();
    }

The first way is better and a must some times. If you only use empty constructors for your members you won't see the difference, except in performance, because the compiler initialize the member by default in its initialisation list ( the part after the ":", if you don't do that, he does it silently for you... ) But if your member doesn't have an empty constructor, for example:

    AnotherClass:: AnotherClass(int a, int b)

if you will try to use the second way on initialisation you will get a message like:

error: constructor for 'Initiator' must explicitly initialize the member 'class2' which does not have a default constructor
ohad edelstain
  • 1,425
  • 2
  • 14
  • 22