2

我想在调用父构造函数之前做一些处理。下面的例子说明了为什么我要这样做,尽管为了清楚起见我已经把它变得微不足道了。真正的父构造函数正在做一些渲染,但让我们先尝试解决这个问题。

本质上,当父构造函数调用覆盖函数但子数据尚未设置时,问题就出现了。我该如何解决?

class BaseClass {

public:
    int myNumber;

    BaseClass(){
        myNumber = 0;
        this->printNumber();
    }

    virtual void printNumber(){
        printf("My number is %d\n", this->myNumber);
    }
}

class ChildClass : BaseClass {

public:
    float childNumber;

    ChildClass(float myNumber) : BaseClass() {
        this->childNumber = myNumber;
    }

    void printNumber(){
        printf("My number is %f\n", this->childNumber);
    }

}

4

6 回答 6

9

当父构造函数调用覆盖函数时,问题就出现了,

No, that never happens. Within the constructor of BaseClass its dynamic type is BaseClass as well, so the printNumber() call will resolve to its own number instead of some derived class. Why? Because at that time the constructors for ChildClass has not yet finished running and so it wasn't yet created.

As @FredLarson comments, here is more info on the subject: http://parashift.com/c++-faq/calling-virtuals-from-ctors.html

于 2013-01-08T19:13:49.287 回答
2

Like others said above, you shouldn't call a virtual member from a constructor. But to address your problem there is an idiom that might help you, it is called base-from-member:

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Base-from-Member

What it does basically, is take advantage of the fact that base classes are initialized in the order they are declared. You may be able to do what you need to do in a separate base class before your base class is initialized.

class OtherBaseClass {
    int Num;
    OtherBaseclass(float num) : Num(num)
    {
        printf("My number is %d\n", this->Num);
    }
};


class ChildClass : OtherBaseClass, BaseClass {

public:
    float childNumber;

    ChildClass(float myNumber) : OtherBaseClass(myNumber), BaseClass() {
....
于 2013-01-08T19:18:17.533 回答
2

Note that the constructor of the base class cannot call to the virtual function version of the derived class, that is, your ChildClass::printNumber() function will not be called.

That said, if you want to execute something before the constructor of the base class, one way to do it is using yet another base class, and put it before the other one:

class RunMeFirst
{
    public RunMeFirst()
    { printf("whatever...\n"); }
};

class ChildClass : private RunMeFirst, public BaseClass 
{ /*...*/ };
于 2013-01-08T19:19:33.397 回答
1

Do you have to implement the rendering functionality in a base class? Could you instead employ composition instead of inheritance. Using composition would allow you to easily control the member initialization order, e.g.:

#include <iostream>

class renderer
{
public:

    renderer(int number)
    {
        std::cout << "Number is " << number << std::endl;
    }
};

class foo
{
public:

    foo()
        : number_(12)
        , renderer_(number_)
    {
    }

private:

    int number_;
    renderer renderer_;

};

int main()
{
    foo bar;
}

In general, prefer composition to inheritance. Also, the Liskov Substitution Principle may be of use here, too, from a design point of view.

于 2013-01-08T19:32:06.893 回答
0

There are several things that run before a base class' constructor:

  • Initialization of arguments
  • Virtual base classes' constructors
  • Other base classes declared earlier's constructors

One commonly used solution is called the "base from member idiom" and involves moving the member to a new base class that is constructed first.

于 2013-01-08T19:24:00.953 回答
0

Thanks to @K-ballo for pointing out the error and because of that I was able to restructure the code a bit to do what I wanted, but as this isnt a technical correct answer to the question either, Ill leave the correct answer as is.

class BaseClass {

public:
    int myNumber;

    BaseClass(bool initialize = true){
        myNumber = 0;
        if (initialize){
            this->initialize();
        }
    }

    void initialize(){
        this->printNumber();
    }

    virtual void printNumber(){
        printf("My number is %d\n", this->myNumber);
    }
}

class ChildClass : BaseClass {

public:
    float childNumber;

    ChildClass(float myNumber) : BaseClass(false) {
        this->childNumber = myNumber;
        this->initialize();
    }

    void printNumber(){
        printf("My number is %f\n", this->childNumber);
    }

}
于 2013-01-08T19:53:07.797 回答