2

基本クラスから派生したクラスがあるとします。ポリモーフィズムを使用すると、(仮想関数を使用して)基本クラスへのポインターを介して派生クラスの関数を呼び出すことができます。つまり、派生クラスのメンバーに、基本クラスのメンバーになりすまして「ふりをする」ことができます。そのことを念頭に置いて、基本クラスがそのコンストラクターで、派生クラスのメンバーを実際に構築することは可能ですか(これにより、基本クラスのメンバーであると「ふりをする」ので、何も壊れません)?論理的には、これが機能しない理由はわかりませんが、構文的にそれを行う方法を理解することはできません。これが不可能な場合は、どうしてですか?

派生クラスのメンバーを作成し、それを基本クラスのメンバーとして返す別の関数を使用できることを知っています。これが不可能になった場合は、これを実行しますが、コンストラクターとして使用する方がクリーンです(これは、この個別の関数が基本的にはそうであるためです)。

編集:これが私が探しているものの例です:

class base
{
  base()
  {
    this=new derived(); //This is what I am looking for
  }

  virtual func();
};

class derived : public base
{
  derived() : base()
    {}

  func()
  {
    ...
  }
};

上記のように、これは次の方法で実現できます。

base *fake_base_constructor()
{
  return new derived();
}

実際には、複数の派生クラスがあり、基本クラスコンストラクターはパラメーターに基づいてそれらから選択しますが、概念的には1つだけ必要です。

4

3 に答える 3

4

不思議なことに繰り返されるテンプレートパターンが必要なようです。基本クラスは、派生型であるテンプレートパラメータを指定することにより、どの型が派生しているのかを知ることができます。

template <class D>
class Base
{ };

class Derived : public Base<Derived>
{ };

Derivedこれで、の定義でテンプレートパラメータを使用できますBase。たとえば、のコンストラクタは次のBaseことを実行できます。

template <class D>
void Base<D>::some_base_member()
{
  D d;
};

実際、このパターンの重要な結果は、クラスDerivedからメンバーを呼び出すことができるということです。Base例えば:

template <class D>
void Base<D>::some_base_member()
{
  static_cast<D*>(this)->some_derived_member();
};
于 2012-12-22T00:26:54.453 に答える
2

コンストラクターに何かを渡すことで得られるもののタイプを選択したいようですね。あなたの例は静的な選択を示していますが、テキストは複数の選択肢があることを示唆しています。具体的には、これをどのように使用するのか興味があります。

次のようなことをしたいとします。

enum Types
{
    Type_A,
    Type_B,
};

class Base
{
public:
    Base(Types t)
    {
        switch (t)
        {
        case Type_A:
            this = <magic> A;
            break;

        case Type_B:
            this = <magic> B;
            break;
        }
    }
};

しかし問題は、これがC++の動作方法ではないということです。オブジェクトのスペースは、コンストラクターがヒットするまでにすでに割り当てられているため、派生アイテムをそのスペースに詰め込むことはできません。たとえば、これをクラスメンバー変数として持っていると想像してください。

struct SomeStruct
{
    int i;
    Base b;
    float f;

    SomeStruct();
}

SomeStruct::SomeStruct() : i(4), b(Type_A), f(3.14f)
{
}

これはうまくいきません。本当に最も簡単な解決策は、ファクトリ関数を使用することだと思われます。それはあなたがポインタを扱わなければならないことを明確にします、そしてそれはプログラマーが見慣れているものです:

Base* BaseFactory(Types t)
{
    switch (t)
    {
    case Type_A:
        return new A;

    case Type_B:
        return new B;
    }
}

不思議なことに繰り返されるテンプレートパターンと似たようなことをすることもできますが、別の回答で提案されているほど簡単ではありません(Wikipediaの記事で取り上げられていないランタイムポリモーフィズムが必要なためです)が、実際には必要ありません。ソリューションに関係なく、これらのものの構築を行うコードモジュールは、作成できるすべての知識を持っている必要があり、制約を考慮すると、基本クラス以外の場所にその知識のバンドルを持っていることがおそらく最善です。私の意見ですが、長い間、大きな紐の束を作るのを避けるのが最も安全だと思います。

于 2012-12-22T02:35:38.427 に答える
0

あなたが尋ねる:

「基本クラスがそのコンストラクターで、派生クラスのメンバーを実際に構築することは可能ですか(これにより、基本クラスのメンバーであると「ふりをする」ので、何も壊れません)?」

はい、基本クラスで派生クラス固有の初期化を行う方法はたくさんあります。

これは、私が書いたブログ投稿の1つの例で、「パーツファクトリを使用して建設後を回避する方法」と題されています。

#include <stdio.h>

namespace apiLevel {
    enum Handle {};

    Handle newButton( char const title[] )
    {
        printf( "apiLevel::newButton(\"%s\")\n", title );
        return Handle();
    }

    Handle newListbox( char const [] = "" )  { return Handle(); }
    Handle newStatic( char const [] = "" )   { return Handle(); }
}  // namespace apiLevel

class Widget
{
private:
    apiLevel::Handle    handle_;

protected:
    struct ApiWidgetFactory
    {
        virtual apiLevel::Handle newWidget( char const title[] ) const
        {
            return apiLevel::newStatic( title );  // Reasonable.
        }
    };

public:
    explicit Widget(
        char const                  title[] = "\"? (unspecified)\"",
        ApiWidgetFactory const&     factory = ApiWidgetFactory()
        )
        : handle_( factory.newWidget( title ) )
    {}
};

class Button
    : public Widget
{
protected:
    struct ApiWidgetFactory: Widget::ApiWidgetFactory
    {
        virtual apiLevel::Handle newWidget( char const title[] ) const
        {
            return apiLevel::newButton( title );    // Derived class specific.
        }
    };

public:
    explicit Button(
        char const                  title[],
        ApiWidgetFactory const&     factory = ApiWidgetFactory()
        )
        : Widget( title, factory )
    {}
};

int main()
{
    Button  button( "Just a button" );
}

上記のアプローチの主な利点は、通常のRAII指向の構造で、クライアントコードへのタイプセーフなインターフェイスを提供することです(つまり、クラスの不変条件をうまく利用します)。

主な欠点は、主にコードをどこに配置するかという問題であるにもかかわらず、2つの並列クラス階層を維持する必要があることです。

詳細については、ブログの投稿を参照してください。このブログの投稿は、FAQにさらにリンクしており、他のいくつかのアプローチも示しています。ブログの投稿は、馬の口、つまり私のものから直接のものです。FAQ項目は、マーシャルにFAQの問題と解決策を書くように説得した結果です。

于 2012-12-22T01:32:07.740 に答える