Maeve Kennedy
Maeve Kennedy

Reputation: 249

Initializing private member variables of a class

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?

Upvotes: 10

Views: 38372

Answers (5)

Emil Laine
Emil Laine

Reputation: 42828

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.

Upvotes: 8

ohad edelstain
ohad edelstain

Reputation: 1645

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

Upvotes: 0

CreativeMind
CreativeMind

Reputation: 917

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

Upvotes: 2

rkachach
rkachach

Reputation: 17325

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

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409136

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.

Upvotes: 2

Related Questions