views:

940

answers:

6

I've trying to achieve something like this:

class Base
{
  public:

  Base(string S) 
  {
  ...
  };
}

class Derived: Base
{
public:
  int foo;
  string bar()
  {
    return stringof(foo); // actually, something more complex
  };

  Derived(int f) : foo(f), Base(bar()) 
  {
  };
}

Now, this doesn't work as I want, because bar() is called in the Derived constructor before foo is initialized.

I considered adding a static function similar to bar() which takes foo as a parameter - and using that in the initialization list, but thought I'd ask if there were any other techniques that could be used to dig myself out of this one...

Edit: Thanks for feedback - here's how I was going to handle the static function. Not sure if the overload between a static and non-static function is too clever, but...

class Derived: Base
{
public:
  int foo;

  static string bar(int f)
  {
    return stringof(f); // actually, something more complex
  }

  string bar()
  {
    return bar(foo); 
  };

  Derived(int f) :  Base(bar(f)) , foo(f)
  {
  };
}
+4  A: 

Yes, using a function (static class method or regular function) that takes foo as a parameter and returns a string is a good solution. You can call this same function from Derived::bar to prevent code duplication. So, your constructor would look like this:

Derived(int f) : Base(stringof(f)), foo(f) {}

I place the call to the Base constructor first in the list to emphasize the order in which the initializations occur. The ordering of the initializer list has no effect as all class members are initialized in the order that they are declared in the class body.

This is a very clean, functional approach to the problem. However, if you still would like to weigh alternatives then consider using composition instead of inheritance for the relationship between the Derived and Base classes:

class Base {
public:
    Base(string S) {  ...  }
    void bat() { ... }
};

class Derived {
    Base *base;
    int foo;

public:
    Derived(int f) : base(NULL), foo(f) {
        base = new Base(bar());
    }
    ~Derived() {
        delete base;
    }

    string bar() {
        return stringof(foo); // actually, something more complex
    }

    void bat() {
        base->bat();
    }
};

You will need to consider the pros and cons for your specific situation. With Derived holding a reference to Base you gain greater control over the initialization order.

Judge Maygarden
hmm combining this with the pimpl idiom would be nice
Johannes Schaub - litb
Nice - however the downside of composition is the number of "bat-like" stubs needed to expose base methods.
Roddy
True! I figured you would stick with plan A in this case for just that reason.
Judge Maygarden
A: 

I've been wanting to do this as well, but I gave up in the end.
Any suitable function call could be used as the parameter of Base().
Another option is to add and alternative constructor to Base that takes an int and does the conversion to 'string' itself.

quamrana
The "alternative constructor" isn't nice, because the conversion logic is fundamentally part of the derived class.
Roddy
Well, I guessed that would be the case, but it was the only alternative to a static function that I could think of.
quamrana
+4  A: 

You can only call static functions in the initializer list. The way you have it in your code:

class Derived: Base
{
public:
  int foo;
  string bar()
  {
    return stringof(foo); // actually, something more complex
  };

  Derived(int f) : foo(f), Base(bar()) 
  {
  };
}

Will still initialize Base first, and then foo. The order of how you write things in an constructor initializer list does not matter in any way. It will always construct in this order:

  1. First, all virtual base classes
  2. Then the non-virtual base classes in the order they appear in the base-classes list
  3. Then all member objects in the order they are defined in the class definition.

Thus, you end up calling stringof with an uninitialized value. This problem is solved in boost::base_from_member. Also note that calling any nonstatic member function before all the constructor initializers of all base-classes completed is undefined behavior.

Calling static functions, however, is totally fine:

class Derived: Base
{
public:
  int foo;
  static string bar(int f)
  {
    return stringof(f); // actually, something more complex
  };

  Derived(int f) : Base(bar(f)), foo(f)
  {
  };
}
Johannes Schaub - litb
+2  A: 

The base class constructor always gets called before initializing the other members of the derived class; your compiler should be giving you a warning for having the initializers in the wrong order. The only correct solution is to make bar() a static method that takes f as a parameter.

Adam Rosenfield
+1  A: 

The constructor is for, well, constructing the object. This means that, until it returns, there isn't an object there, and therefore that calling member functions just isn't going to work reliably. As everybody else says, use a static function or a non-member function.

David Thornley
Not strictly true, because it's common practice for constructors to call non-static methods - eg, "init()". Initialization lists are a little different.
Roddy
A: 

Just move your constructor code to an Initialize() function and call it from the constructor. This is much simpler than static/nonstatic overriding or anything like that.

lacker
you're missing the point. The *base class* can't be constructed correctly in the way you describe.
Roddy