1

したがって、さまざまなオンラインソースから、コンストラクター内から仮想関数を呼び出すことは一般的にノーノーであることがわかっています。ここでの問題は、基本クラスが最初に構築され、C++が最初に基本クラスのバージョンの関数を呼び出すことです。ただし、これで問題ない可能性のある独自のユースケースがあります。コメントをいただければ幸いです。この状況を考えてみましょう。

class Base
{
public:
    Base(string data)
    {
        Parse(data);
    }
    ~Base(){}
private:
    virtual Parse(string data);
}

class Derived : public Base
{
public:
    Derived(string data)
    {
        Parse(data);
    }
    ~Derived();
private:
    Parse(string data);
}

このような設定があり、各派生クラスの予想される動作は次のとおりであるとしましょう。

  1. 基本クラスでParseが呼び出され、これらすべての入力文字列に共通するものを解析します。
  2. 派生解析では、派生クラスに固有のデータを取得する必要があります。

この場合、コンストラクターで仮想関数を使用するのは理にかなっていますか?または、このクラスを作成するたびに「解析」を公開して呼び出す必要がありますか?または他の提案があります。

これが理にかなっていることを願っています。上記の構文エラーはご容赦ください。私は一般的な考え方を表現しようとしています。

4

5 に答える 5

1

または、このクラスを作成するたびに「解析」を公開して呼び出す必要がありますか?

実際、このシナリオでは、ポリモーフィックな動作を避けたいのでParse、仮想メソッドを作成する必要がある理由、またはクラスの非静的メソッドを作成する必要がある理由がわかりません。これは、クラスのデータメンバーを変更しないためです。クラス自体...たとえばParse、プライベートstaticメソッドとして使用しClassType::Parse()、各オブジェクトのコンストラクターを呼び出すだけで、同じ機能を利用できます。

于 2012-06-27T17:28:08.257 に答える
1

コンストラクターで仮想関数を使用することは、それが機能する限り、まったく問題ありません。コンストラクターから呼び出されたときの仮想関数のポリモーフィックな動作は、常に階層全体の既に構築されたサブセットに制限されることを覚えておくことが重要です。(デストラクタにも同様のルールが適用されます)。

この制限された仮想動作が目的に適している場合、彼らは必ずそれを使用します。

あなたが参照しなければならない「no-no」引数は、ユーザーが[まだ構築されていない]派生クラスの関数が呼び出されることを期待しているという人為的な前提に基づいた、よく知られた偽の引数です。仮想関数はコンストラクターから呼び出されるべきではないという結論に、その発明された誤った前提を翻訳する人がいる理由は、私にはわかりません。納得のいく説明はまだ見たことがありません。

于 2012-06-27T18:38:53.933 に答える
0

コンストラクターから仮想関数を呼び出さないでください。基本クラスの仮想テーブルが使用されるため、ポリモーフィックな動作は得られません。

ポリモーフィックな振る舞いが必要ない場合-関数を仮想化しないでください

于 2012-06-27T17:27:16.587 に答える
0

解決策は非常に簡単です。

class Base
{
public:
    Base(string data)
    {
        Parse(data);
    }
    ~Base(){}
private:
    void Parse(string data);
}

class Derived : public Base
{
public:
    Derived(string data)
    {
        ParseMore(data);
    }
    ~Derived();
private:
    void ParseMore(string data);
}

Derived構築されると、 のコンストラクタに入る前にBaseのコンストラクタが呼び出されます。したがって、Base で行われる解析は終了し、Derived コンストラクターで解析を終了できます。Derived

于 2012-06-27T18:27:51.243 に答える
0

Parserこれに対する最も簡単な解決策は、戦略パターンを使用することです。純粋仮想関数を使用して抽象基本クラスを定義しparse、派生クラスにパーサーのインスタンスへのポインターを基本クラス コンストラクターに渡します。すなわち:

class Base
{
protected:
    class Parser
    {
    public:
        virtual ~Parser() {}    // Probably not necessary, since no
                                // one is going to dynamically
                                // allocate any of these, but better
                                // safe than sorry.
        virtual void parse( std::string const& data ) const = 0;
    };

    Base( Parser const& derivedClassParser, std::string const& data )
    {
        derivedClassParser.parse( data );
    }
public:
    //  ...
};

派生クラスのそれぞれは、 から派生したパーサーを定義し、その Base::Parser静的インスタンスを定義し、この静的インスタンスのアドレスを基本クラスに渡します。

別の可能性があります。オブジェクトの一時的なインスタンスがある場合、必ずしも正しく機能するとは限りませんが、何らかの理由で上記のパターンを使用できない場合に役立ちます。基本的に、デストラクタで仮想関数を呼び出す特別なクラスを定義し、暗黙的な変換をstd::string行い (char const*文字列リテラルの受け渡しをサポートするため)、コンストラクタを宣言してこのクラスのインスタンスを取得します。例えば:

class Base
{
public:
    class CallVirtual
    {
        std::string myData;
        mutable Base* myOwner;
        friend class Base;
    public:
        CallVirtual( std::string const& data )
            : myData( data )
            , myOwner( NULL )
        {
        }
        ~CallVirtual()
        {
            if ( myOwner != NULL ) {
                myOwner->Parse( myData );
            }
        }
    };

    Base( CallVirtual const& dataArg )
    {
        dataArg.myOwner = this;
        //  ...
    }

    virtual void Parse( std::string const& data ) ...
};

CallVirtual const&派生クラスもas 引数を取る必要があります。次に、派生クラスのインスタンスを作成すると、次のようになります。

Base* p = new Derived( someString );

、文字列は自動的に一時的な に変換されCallVirtual、そのデストラクタは完全な式の最後で呼び出されます。

于 2012-06-27T18:41:27.610 に答える