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

オーバーライドされた関数が親コンストラクターによって呼び出されると、問題が発生します。

いいえ、それは決して起こりません。BaseClass動的型のコンストラクター内でBaseClassも同様であるため、呼び出しは派生クラスではなくprintNumber()独自の番号に解決されます。なんで?その時点でのコンストラクターChildClassはまだ実行を終了していないため、まだ作成されていません。

@FredLarsonのコメントとして、この件に関する詳細情報は次のとおりです。http://parashift.com/c++-faq/calling-virtuals-from-ctors.html

于 2013-01-08T19:13:49.287 に答える
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() {
....
于 2013-01-08T19:18:17.533 に答える
2

基本クラスのコンストラクターは、派生クラスの仮想関数バージョンを呼び出すことができないことに注意してください。つまり、ChildClass::printNumber()関数は呼び出されません。

つまり、基本クラスのコンストラクターの前に何かを実行したい場合、それを行う 1 つの方法は、さらに別の基本クラスを使用し、それを別の基本クラスの前に配置することです。

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

class ChildClass : private RunMeFirst, public BaseClass 
{ /*...*/ };
于 2013-01-08T19:19:33.397 に答える
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;
}

一般に、継承より合成を優先します。また、リスコフの置換原理は、設計の観点からもここで役立つ場合があります。

于 2013-01-08T19:32:06.893 に答える
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);
    }

}
于 2013-01-08T19:53:07.797 に答える
0

基本クラスのコンストラクターの前に実行されるものがいくつかあります。

  • 引数の初期化
  • 仮想基底クラスのコンストラクター
  • 以前のコンストラクターで宣言された他の基本クラス

一般的に使用されるソリューションの 1 つは、「メンバーからのベース」イディオムと呼ばれ、最初に構築される新しいベース クラスにメンバーを移動することです。

于 2013-01-08T19:24:00.953 に答える