如何在处理之后调用父构造函数?

时间:2013-01-08 19:11:15

标签: c++

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

本质上,当父构造函数调用overriden函数但子目标数据尚未设置时,会出现问题。我该如何解决这个问题?

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);
    }

}

6 个答案:

答案 0 :(得分:9)

  

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

不,这从未发生过。在BaseClass的构造函数中,其动态类型也是BaseClass,因此printNumber()调用将解析为其自己的数字而不是某些派生类。为什么?因为当时ChildClass的构造函数尚未完成运行,因此尚未创建。

作为 @FredLarson 评论,以下是有关此主题的更多信息:http://parashift.com/c++-faq/calling-virtuals-from-ctors.html

答案 1 :(得分:2)

与上面说的其他人一样,你不应该从构造函数中调用虚拟成员。但是为了解决你的问题,有一个可能对你有帮助的习惯用法,它被称为base-from-member:

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

它基本上做的是利用基类按声明的顺序初始化的事实。在初始化基类之前,您可以在单独的基类中执行您需要执行的操作。

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() {
....

答案 2 :(得分:2)

请注意,基类的构造函数无法调用派生类的虚函数版本,也就是说,您的ChildClass::printNumber()函数不会被调用。

也就是说,如果你想在基类的构造函数之前执行某些操作,一种方法是使用另一个基类,并将它放在另一个基类之前:

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

class ChildClass : private RunMeFirst, public BaseClass 
{ /*...*/ };

答案 3 :(得分:1)

您是否必须在基类中实现呈现功能?您可以改为使用合成而不是继承。使用合成将允许您轻松控制成员初始化顺序,例如:

#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;
}

一般来说,prefer composition to inheritance。此外,从设计的角度来看,Liskov Substitution Principle也可能在这里使用。

答案 4 :(得分:0)

在基类构造函数之前运行了几件事:

  • 参数初始化
  • 虚拟基类的构造函数
  • 其他基类声明了前面的构造函数

一种常用的解决方案称为“基于成员习语”,并涉及将成员移动到首先构造的新基类。

答案 5 :(得分:0)

感谢@ K-ballo指出错误,因此我能够重新构建代码以执行我想要的操作,但由于这不是问题的技术正确答案,我会留下正确的答案按原样回答。

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);
    }

}