Author Topic: Class contructors and initialization lists (C++)  (Read 2535 times)

0 Members and 1 Guest are viewing this topic.

Offline z64555

  • 210
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
Class contructors and initialization lists (C++)
I came across this info after searching for a way to get a few wxWidgets classes to initialize properly when put as a member of another class or when I needed to expand the functionality of a wxWidgets class. Since wxWidgets is what I call "aggressively OOP,"  many of its classes do not have a default constructor, and require a initialization list to work correctly. This is the article that finally shed light on how initialization lists work : http://www.cprogramming.com/tutorial/initialization-lists-c++.html

As a writing exercise, I wrote up a few paragraphs in my own words. It's been a long time since I last made a tutorial, so please forgive me if things seem a bit out of whack.  :nervous:
--Z



Did you ever wonder why classes have to have their own constructors, even when they inherit at least one other class or structure? The truth is that child classes first call their parent class's before going into their own constructor, essentially meaning that the child classes "tack on" whatever methods and members they have into an instance of their parent class.

If a child class has multiple parents, then the parents' constructors are called in the sequence that they are listed. Also, if the parent class is itself a child or derived class, then the parent classes are called in sequence from the base class down.

This mechanism is usually done by the C++ compiler, and the programmer does not have to worry if all of the parent classes have their own default constructor. However, if any of the parent classes do NOT have a default constructor, then the programmer must intervene and tell the compiler which constructor to use.

This is done via an initialization list:
Code: [Select]
// Header file
class parent
{
public:
  parent( int a );  // Non-default Constructor specified. Compiler won't make a default constructor.
private:
  int parent_var;
};

class child : public parent
{
public:
  child( int b )
private:
  int child_var;
};
Code: [Select]
// Implementation File
parent::parent( int a )
{
  parent_var = a;
}

child::child( int b )
  : parent( 0 )
{
  child_var = b;
}

The "  : parent( 0 )" is what is called an initialization list. This list specifies which constructors to call before the child constructor is even entered. Going back to our example, when an instance of the child class is made, it will have a parent_var with a value of 0 and a child_var with the value of b. However, since parent_var is specified as a private member of the parent class, it is not accessible by any methods in the child class or any other derived. As a reminder, protected methods and members are accessible by any methods in any methods inside the parent class and any of the child classes.

The initialization list is also handy for calling constructors of classes that are member of the class that is being constructed. For example:
Code: [Select]
// Header file
class Kung
{
public:
  Kung( int a );
private:
  int kung_var;
}

class Bar
{
public:
  Bar( int b );
private:
  Kung foo;
  int bar_var;
}
Code: [Select]
// Implementation file
Kung::Kung( int a )
{
  kung_var = a;
}

Bar::Bar( int b )
  : foo( b )  // Note: this isn't the name of the class type, this is the name of the member
{
}

Which ends up with the kung_var with whatever value b has.

As a closing note, the initialization list can also initialize members that are standard types along with the constructors:
Code: [Select]
// foo's constructor is called, and then bar_var is set to 42;
Bar::Bar( int b )
  : foo( b ), bar_var( 42 )  // By convention, the members are initialized in the same order they are specified.
                             // Some compilers allow you to specify them in any order, but remember that they are initialized in order they show up here!
{
}
« Last Edit: November 19, 2012, 11:26:57 am by z64555 »
Secure the Source, Contain the Code, Protect the Project
chief1983

------------
funtapaz: Hunchon University biologists prove mankind is evolving to new, higher form of life, known as Homopithecus Juche.
z64555: s/J/Do
BotenAlfred: <funtapaz> Hunchon University biologists prove mankind is evolving to new, higher form of life, known as Homopithecus Douche.

 

Offline Aardwolf

  • 211
  • Posts: 16,384
    • Minecraft
Re: Class contructors and initialization lists (C++)
Also useful because you can do stuff like this:

Code: [Select]
struct Vec3
{
float x, y, z;
Vec3(float x, float y, float z) : x(x), y(y), z(z) { }
};

:D

 

Offline z64555

  • 210
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
Re: Class contructors and initialization lists (C++)
Yep. I think the convention for struct constructors and  initialization lists is to have them in the struct declaration (such as in a .h), like you did there.

For classes, the initialization list should be with the constructor's definition (such as in a .cpp), like the examples I gave in the OP.
Secure the Source, Contain the Code, Protect the Project
chief1983

------------
funtapaz: Hunchon University biologists prove mankind is evolving to new, higher form of life, known as Homopithecus Juche.
z64555: s/J/Do
BotenAlfred: <funtapaz> Hunchon University biologists prove mankind is evolving to new, higher form of life, known as Homopithecus Douche.

 
Re: Class contructors and initialization lists (C++)
Quote
  : foo( b ), bar_var( 42 )  // By convention, the members are initialized in the same order they are specified.
                             // Some compilers allow you to specify them in any order, but remember that they are initialized in order they show up here!
Are you sure? Compile and try this:

Code: [Select]
#include <iostream>

class Foo {
int a;
int b;
public:
Foo(int c) : b(c), a(b) {
   std::cout<<a<<'\n'<<b<<std::endl;
}
};

class Foo2 {
int b; //order of declaration switched
int a;
public:
Foo2(int c) : b(c), a(b) { //this stays the same
   std::cout<<a<<'\n'<<b<<std::endl;
}
};

int main() {
Foo f(3); //This does NOT assign 3 to b, then b (which is 3) to a. First a gets the (random) value of b, then b is assigned 3
Foo2 f2(3); //In this case, both a and b get the value 3
}
//output (on my compiler) during my last try:
//3547936
//3
//3
//3
The order in the initializer list has absolutely no impact on the order of initialization. That gets defined by the order of declaration.

Another thing I want to add: You can call functions in the initializer list!
Code: [Select]
#include <iostream>


int Faculty(int n) {
   if(n==1)
      return 1;
   return n*Faculty(n-1);
}

class Foo {
int a;
public:
Foo(int c) :a(Faculty(c)) {
   std::cout<<a<<std::endl;
}
};


int main() {
Foo f(5);
} //output: 120

 

Offline z64555

  • 210
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
Re: Class contructors and initialization lists (C++)
Quote
  : foo( b ), bar_var( 42 )  // By convention, the members are initialized in the same order they are specified.
                             // Some compilers allow you to specify them in any order, but remember that they are initialized in order they show up here!
Are you sure? Compile and try this:

Code: [Select]
#include <iostream>

class Foo {
int a;
int b;
public:
Foo(int c) : b(c), a(b) {
   std::cout<<a<<'\n'<<b<<std::endl;
}
};

class Foo2 {
int b; //order of declaration switched
int a;
public:
Foo2(int c) : b(c), a(b) { //this stays the same
   std::cout<<a<<'\n'<<b<<std::endl;
}
};

int main() {
Foo f(3); //This does NOT assign 3 to b, then b (which is 3) to a. First a gets the (random) value of b, then b is assigned 3
Foo2 f2(3); //In this case, both a and b get the value 3
}
//output (on my compiler) during my last try:
//3547936
//3
//3
//3
The order in the initializer list has absolutely no impact on the order of initialization. That gets defined by the order of declaration.


Is this behavior defined in the official C++ language specification? I don't exactly have access to the docs just yet (haven't gotten around to buying a copy :nervous:)
Secure the Source, Contain the Code, Protect the Project
chief1983

------------
funtapaz: Hunchon University biologists prove mankind is evolving to new, higher form of life, known as Homopithecus Juche.
z64555: s/J/Do
BotenAlfred: <funtapaz> Hunchon University biologists prove mankind is evolving to new, higher form of life, known as Homopithecus Douche.

 

Offline Aardwolf

  • 211
  • Posts: 16,384
    • Minecraft
Re: Class contructors and initialization lists (C++)
I know g++ warned if I had them ordered differently, but idunno if that's spec or compiler-specific.

 
Re: Class contructors and initialization lists (C++)
You can use the final draft. Yes, it is defined like that in the standard:
Quote
Initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in
the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes,
where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
(regardless of the order of the mem-initializers).
— Then, non-static data members are initialized in the order they were declared in the class definition
(again regardless of the order of the mem-initializers).

— Finally, the compound-statement of the constructor body is executed.

 
Re: Class contructors and initialization lists (C++)
This is probably something you should use whenever reasonably possible. Aside from enabling you to inherit from a class without a default constructor, it is also the only way to initialize constant class members:

Code: [Select]
class A {
    const int a;
    public:
        A(int a) : a(a + 5) {}
//      B(int b) { a = b; } obviously wouldn't compile
};

It also avoids double initialization: unless you override it, a constructor always calls all the class members', and all the base classes' default constructors, whether you need it or not - it has to, all the objects have to be constructed once you enter the constructor's body.
Of course, that probably won't be a problem performance-wise, but is conceptually a horribly messy way to do things.

Finally there's also this neat feature of the new C++ standard: http://en.wikipedia.org/wiki/C%2B%2B0x#Object_construction_improvement, already implemented in gcc.
The lyf so short, the craft so long to lerne.