20

C ++では、オブジェクトのタイプを照会し、その情報を使用して同じタイプの新しいオブジェクトを動的に作成する方法はありますか?

たとえば、単純な3つのクラス階層があるとします。

class Base
class Foo : public Base
class Bar : public Base

ここで、タイプBaseとしてキャストされたオブジェクトを提供するとします。これは実際にはタイプFooです。タイプをクエリし、その情報を使用して後でタイプFooの新しいオブジェクトを作成する方法はありますか?

4

8 に答える 8

11

クローン方式

タイプをクエリし、その情報から構築できる言語によって提供されるものはありませんが、さまざまな方法でクラス階層に機能を提供できます。最も簡単な方法は、仮想メソッドを使用することです。

struct Base {
  virtual ~Base();
  virtual std::auto_ptr<Base> clone(/*desired parameters, if any*/) const = 0;
};

これは少し異なることをします:現在のオブジェクトのクローンを作成します。これは多くの場合必要なものであり、オブジェクトをテンプレートとして保持し、必要に応じて複製および変更することができます。

Tronicを拡張すると、クローン関数を生成することもできます。

なぜauto_ptrしたがって、 newを使用してオブジェクトを割り当て、所有権の譲渡を明示的にすることができます。呼び出し元は、 deleteによってオブジェクトの割り当てを解除する必要があることは間違いありません。例えば:

Base& obj = *ptr_to_some_derived;
{ // since you can get a raw pointer, you have not committed to anything
  // except that you might have to type ".release()"
  Base* must_free_me = obj.clone().release();
  delete must_free_me;
}
{ // smart pointer types can automatically work with auto_ptr
  // (of course not all do, you can still use release() for them)
  boost::shared_ptr<Base> p1 (obj.clone());
  auto_ptr<Base>          p2 (obj.clone());
  other_smart_ptr<Base>   p3 (obj.clone().release());
}
{ // automatically clean up temporary clones
  // not needed often, but impossible without returning a smart pointer
  obj.clone()->do_something();
}

オブジェクトファクトリ

要求どおりに実行し、インスタンスとは独立して使用できるファクトリを取得したい場合は、次のようにします。

struct Factory {}; // give this type an ability to make your objects

struct Base {
  virtual ~Base();
  virtual Factory get_factory() const = 0; // implement in each derived class
    // to return a factory that can make the derived class
    // you may want to use a return type of std::auto_ptr<Factory> too, and
    // then use Factory as a base class
};

get_factoryは同じ役割の半分を果たし、戻り型(およびその意味)が唯一の違いであるため、クローンメソッドの場合と同じロジックと機能の多くを使用できます。

工場についてもすでに 回取り上げました。SimpleFactoryクラスを適応させて、ファクトリオブジェクト(get_factoryによって返される)がグローバルファクトリへの参照と、作成に渡すパラメータ(たとえば、クラスの登録名-boost ::functionboost::bindを適用する方法を検討する)を保持するようにすることができます。これを使いやすくします)。

于 2010-01-09T09:15:33.500 に答える
6

基本クラスによってオブジェクトのコピーを作成するために一般的に使用される方法は、基本的にポリモーフィック コピー コンストラクターである clone メソッドを追加することです。通常、この仮想関数はすべての派生クラスで定義する必要がありますが、Curiously Recurring Template Patternを使用すると、コピー&ペーストを回避できます。

// Base class has a pure virtual function for cloning
class Shape {
public:
    virtual ~Shape() {} // Polymorphic destructor to allow deletion via Shape*
    virtual Shape* clone() const = 0; // Polymorphic copy constructor
};
// This CRTP class implements clone() for Derived
template <typename Derived> class Shape_CRTP: public Shape {
public:
    Shape* clone() const {
        return new Derived(dynamic_cast<Derived const&>(*this));
    }
};
// Every derived class inherits from Shape_CRTP instead of Shape
// Note: clone() needs not to be defined in each
class Square: public Shape_CRTP<Square> {};
class Circle: public Shape_CRTP<Circle> {};
// Now you can clone shapes:
int main() {
    Shape* s = new Square();
    Shape* s2 = s->clone();
    delete s2;
    delete s;
}

すべての派生クラスで同じになるが、派生型の知識が必要な機能には、同じ CRTP クラスを使用できることに注意してください。これには、clone() 以外にも多くの用途があります。たとえば、二重ディスパッチです。

于 2010-01-09T10:28:40.157 に答える
3

これを行うには、いくつかのハッキーな方法しかありません。

最初の、そして私見で最も醜いのは:

Base * newObjectOfSameType( Base * b )
{
  if( dynamic_cast<Foo*>( b ) ) return new Foo;
  if( dynamic_cast<Bar*>( b ) ) return new Bar;
}

これは、RTTIが有効になっていて、Baseに仮想機能が含まれている場合にのみ機能することに注意してください。

2つ目の優れたバージョンは、純粋な仮想クローン関数を基本クラスに追加することです。

struct Base { virtual Base* clone() const=0; }
struct Foo : public Base { Foo* clone() const { return new Foo(*this); }
struct Bar : public Base { Bar* clone() const { return new Bar(*this); }

Base * newObjectOfSameType( Base * b )
{
  return b->clone();
}

これはとてもきれいです。

これに関するクールで興味深い点の1つは 、をFoo::clone返しFoo*、一方をBar::clone返すことBar*です。これで問題が発生することが予想されるかもしれませんが、共変リターン型と呼ばれるC ++の機能により、すべてが機能します。

残念ながら、共変の戻り型はスマートポインターでは機能しないためsharted_ptrs、コードを使用すると次のようになります。

struct Base { virtual shared_ptr<Base> clone() const=0; }
struct Foo : public Base { shared_ptr<Base> clone() const { return shared_ptr<Base>(new Foo(*this) ); }
struct Bar : public Base { shared_ptr<Base> clone() const { return shared_ptr<Base>(new Bar(*this)); }

shared_ptr<Base> newObjectOfSameType( shared_ptr<Base> b )
{
  return b->clone();
}
于 2010-01-09T09:17:57.137 に答える
1

たとえばtypeid、オブジェクトの動的型を照会するために使用できますが、型情報から新しいオブジェクトを直接インスタンス化する方法がわかりません。

cloneただし、上記のアプローチとは別に、ファクトリを使用できます。

#include <typeinfo>
#include <iostream>

class Base
{
public:
    virtual void foo() const
    {
        std::cout << "Base object instantiated." << std::endl;
    }
};


class Derived : public Base
{
public:
    virtual void foo() const
    {
        std::cout << "Derived object instantiated." << std::endl;
    }
};


class Factory
{
public:
    static Base* createFrom( const Base* x )
    {
        if ( typeid(*x) == typeid(Base) )
        {
            return new Base;
        }
        else if ( typeid(*x) == typeid(Derived) )
        {
            return new Derived;
        }
        else
        {
            return 0;
        }
    }
};


int main( int argc, char* argv[] )
{
    Base* X = new Derived;
    if ( X != 0 )
    {
        std::cout << "X says: " << std::endl;
        X->foo();
    }

    Base* Y = Factory::createFrom( X );
    if ( Y != 0 )
    {
        std::cout << "Y says: " << std::endl;
        Y->foo();
    }

    return 0;
}

PS:このコード例の本質的な部分は、もちろんFactory::createFromメソッドです。(私のC ++は少し錆びているので、おそらく最も美しいC ++コードではありません。ファクトリメソッドは、考え直して、静的であってはなりません。)

于 2010-01-09T09:26:00.397 に答える
1

プロジェクトでマクロを使用して、そのようなメソッドを合成しました。私は今このアプローチを研究しているだけなので、間違っているかもしれませんが、IAllocable.hh の私のコードであなたの質問への回答があります。私はGCC 4.8を使用していますが、4.7が合うことを願っています。

#define SYNTHESIZE_I_ALLOCABLE \
    public: \
    auto alloc() -> __typeof__(this) { return new (__typeof__(*this))(); } \
    IAllocable * __IAllocable_alloc() { return new (__typeof__(*this))(); } \
    private:


class IAllocable {
public:
    IAllocable * alloc() {
        return __IAllocable_alloc();
    }
protected:
    virtual IAllocable * __IAllocable_alloc() = 0;
};

使用法:

class Usage : public virtual IAllocable {

    SYNTHESIZE_I_ALLOCABLE

public:
    void print() {
        printf("Hello, world!\n");
    }
};

int main() {
    {
        Usage *a = new Usage;
        Usage *b = a->alloc();

        b->print();

        delete a;
        delete b;
    }

    {
        IAllocable *a = new Usage;
        Usage *b = dynamic_cast<Usage *>(a->alloc());

        b->print();

        delete a;
        delete b;
    }
 }

それが役に立てば幸い。

于 2012-07-20T05:39:19.213 に答える
0

C ++では、オブジェクトのタイプを照会する方法はありますか...

はい、typeid()演算子を使用します

例えば:

// typeid, polymorphic class
 #include <iostream>
 #include <typeinfo>
 #include <exception>
 using namespace std;

 class CBase { virtual void f(){} };
 class CDerived : public CBase {};

 int main () {
   try {
     CBase* a = new CBase;
     CBase* b = new CDerived;
      cout << "a is: " << typeid(a).name() << '\n';
     cout << "b is: " << typeid(b).name() << '\n';
     cout << "*a is: " << typeid(*a).name() << '\n';
     cout << "*b is: " << typeid(*b).name() << '\n';
    } catch (exception& e) { cout << "Exception: " << e.what() << endl; }
    return 0;
  }

出力

a is: class CBase *
b is: class CBase *
*a is: class CBase
*b is: class CDerived

typeidが評価するタイプが、間接参照演算子(*)が前に付いたポインターであり、このポインターの値がnullの場合、typeidはbad_typeid例外をスローします。

続きを読む.....

于 2010-01-09T09:08:38.303 に答える
0
class Base
{
public:
 virtual ~Base() { }
};

class Foo : public Base
{

};

class Bar : public Base
{

};

template<typename T1, typename T2>
T1* fun(T1* obj)
{
 T2* temp = new T2();
 return temp;
}

int main()
{
  Base* b = new Foo();
  fun<Base,Foo>(b);
}
于 2010-01-09T10:50:47.653 に答える