views:

27267

answers:

7

What are the C++ rules for calling the superclass constructor from a subclass one??

For example I know in Java, you must do it as the first line of the subclass constructor (and if you don't an implicit call to a no-arg super constructor is assumed - giving you a compile error if that's missing).

+33  A: 

Base class constructors are automatically called for you if they have no argument. If you want to call a superclass constructor with an argument, you must use the subclass' constructor initialization list. Unlike Java, C++ supports multiple inheritance (for better or worse), so the base class must be referred to by name, rather than "super()".

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }


};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

More info on the constructor's initialization list here and here.

luke
I removed 'explicit' from the SuperClass constructor. Despite being a best practice for single-argument constructors, it wasn't germane to the discussion at hand. For more info on the explicit key word, see: http://weblogs.asp.net/kennykerr/archive/2004/08/31/Explicit-Constructors.aspx
luke
+2  A: 

If you have a constructor without arguments it will be called before the derived class constructor gets executed.

If you want to call a base-constructor with arguments you have to explicitly write that in the derived constructor like this:

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

You cannot construct a derived class without calling the parents constructor in C++. That either happends automatically if it's a non-arg C'tor, it happends if you call the derived constructor directly as shown above or your code won't compile.

Nils Pipenbrinck
+1  A: 

The only way to pass values to a parent constructor is through an initialization list. The initilization list is implemented with a : and then a list of classes and the values to be passed to that classes constructor.

Class2::Class2(string id) : Class1(id) {
....
}

Also remember that if you have a constructor that takes no parameters on the parent class, it will be called automatically prior to the child constructor executing.

CR
+14  A: 

In C++, the no-argument constructors for all superclasses and member variables are called for you, before entering your constructor. If you want to pass them arguments, there is a separate syntax for this called "constructor chaining", which looks like this:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

If anything run at this point throws, the bases/members which had previously completed construction have their destructors called and the exception is rethrown to to the caller. If you want to catch exceptions during chaining, you must use a function try block:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

In this form, note that the try block is the body of the function, rather than being inside the body of the function; this allows it to catch exceptions thrown by implicit or explicit member and base class initializations, as well as during the body of the function. However, if a function try block does not throw a different exception, the runtime will rethrow the original error; exceptions during initialization cannot be ignored.

puetzk
I'm not sure I understand the syntax of your second example... Is the try/catch construct a replacement for the constructor body?
levik
Yes. I've reworded the section, and fixed a mistake (the try keyword goes before the initialization list). I should have looked it up instead of writing from memory, it's not something that gets used often :-)
puetzk
+1  A: 
CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }
Dynite
+5  A: 

In C++ there is a concept of constructor's initialization list, which is where you can and should call the base class' constructor and where you should also initialize the data members. The initialization list comes after the constructor signature following a colon, and before the body of the constructor. Let's say we have a class A:


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

Then, assuming B has a constructor which takes an int, A's constructor may look like this:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

As you can see, the constructor of the base class is called in the initialization list. Initializing the data members in the initialization list, by the way, is preferable to assigning the values for b_, and c_ inside the body of the constructor, because you are saving the extra cost of assignment.

Keep in mind, that data members are always initialized in the order in which they are declared in the class definition, regardless of their order in the initialization list. To avoid strange bugs, which may arise if your data members depend on each other, you should always make sure that the order of the members is the same in the initialization list and the class definition. For the same reason the base class constructor must be the first item in the initialization list. If you omit it altogether, then the default constructor for the base class will be called automatically. In that case, if the base class does not have a default constructor, you will get a compiler error.

Dima
Wait a second... You say initializers save on the cost of assignments. But don't the same assignments take place inside them if called?
levik
Nope. Init and assignment are different things. When a constructor is called, it will try to initialize every data member with whatever it thinks is the default value. In the init list you get to supply default values. So you incur initialization cost in either case.
Dima
And if you use assignment inside the body, then you incur the initialization cost anyway, and then the cost of assignment on top of that.
Dima
A: 

class demo{ int data member; //class data memeber demo(int datamemeber) //constuctor defination with initial parameters { }

deepak