从动态类型信息创建新对象

时间:2022-09-25 08:54:29

In C++, is there any way to query the type of an object and then use that information to dynamically create a new object of the same type?

在C ++中,有没有办法查询对象的类型,然后使用该信息动态创建相同类型的新对象?

For example, say I have a simple 3 class hierarchy:

例如,假设我有一个简单的3类层次结构:

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

Now suppose I give you an object cast as type Base -- which is in reality of type Foo. Is there a way to query the type and use that info to later create new objects of type Foo?

现在假设我给你一个类型为Base的对象 - 实际上是Foo类型。有没有办法查询类型并使用该信息以后来创建Foo类型的新对象?

8 个解决方案

#1


12  

Clone method

There is nothing provided by the language that queries type and lets you construct from that information, but you can provide the capability for your class heirarchy in various ways, the easiest of which is to use a virtual method:

查询类型的语言没有提供任何内容,并允许您从该信息构造,但您可以通过各种方式为类层次结构提供功能,最简单的方法是使用虚拟方法:

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

This does something slightly different: clone the current object. This is often what you want, and allows you to keep objects around as templates, which you then clone and modify as desired.

这有点不同:克隆当前对象。这通常是您想要的,并允许您将对象保留为模板,然后根据需要进行克隆和修改。

Expanding on Tronic, you can even generate the clone function.

扩展Tronic,您甚至可以生成克隆功能。

Why auto_ptr? So you can use new to allocate the object, make the transfer of ownership explicit, and the caller has no doubt that delete must deallocate it. For example:

为什么选择auto_ptr?因此,您可以使用new来分配对象,使所有权转移显式,并且调用者毫无疑问删除必须释放它。例如:

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

Object factory

If you'd prefer to do exactly as you asked and get a factory that can be used independently of instances:

如果你更愿意完全按照你的要求去做一个可以独立于实例使用的工厂:

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

Much of the same logic and functionality can be used as for a clone method, as get_factory fulfills half of the same role, and the return type (and its meaning) is the only difference.

许多相同的逻辑和功能可以用作克隆方法,因为get_factory实现了相同角色的一半,并且返回类型(及其含义)是唯一的区别。

I've also covered factories a couple times already. You could adapt my SimpleFactory class so your factory object (returned by get_factory) held a reference to a global factory plus the parameters to pass to create (e.g. the class's registered name—consider how to apply boost::function and boost::bind to make this easy to use).

我已经几次覆盖了工厂。您可以调整我的SimpleFactory类,以便您的工厂对象(由get_factory返回)保存对全局工厂的引用以及要传递给create的参数(例如,类的注册名称 - 考虑如何将boost :: function和boost :: bind应用于使这个易于使用)。

#2


6  

The commonly used way to create copies of objects by base class is adding a clone method, which is essentially a polymorphic copy constructor. This virtual function normally needs to be defined in every derived class, but you can avoid some copy&paste by using the 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;
}

Notice that you can use the same CRTP class for any functionality that would be the same in every derived class but that requires knowledge of the derived type. There are many other uses for this besides clone(), e.g. double dispatch.

请注意,您可以将相同的CRTP类用于在每个派生类中相同但需要了解派生类型的任何功能。除了clone()之外,还有许多其他用途。双重调度。

#3


3  

There's only some hacky ways to do this.

只有一些黑客的方法来做到这一点。

The first and IMHO the ugliest is:

第一个和恕我直言最丑陋的是:

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

Note that this will only work if you have RTTI enabled and Base contains some virtual function.

请注意,这仅在启用了RTTI且Base包含某些虚拟功能时才有效。

The second an neater version is to add a pure virtual clone function to the base class

第二个更简洁的版本是向基类添加纯虚拟克隆功能

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

This is much neater.

这个更整洁。

One cool/interesting thing about this is that Foo::clone returns a Foo*, while Bar::clone returns a Bar*. You might expect this to break things, but everything works due to a feature of C++ called covariant return types.

一个很酷/有趣的事情是Foo :: clone返回一个Foo *,而Bar :: clone返回一个Bar *。你可能希望这会破坏一些东西,但是由于C ++的一个叫做协变返回类型的特性,一切都可以运行。

Unfortunately covariant return types don't work for smart pointers, so using sharted_ptrs your code would look like this.

不幸的是,协变返回类型不适用于智能指针,所以使用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();
}

#4


1  

In C++, is there any way to query the type of an object...

在C ++中,有没有办法查询对象的类型...

Yes, use typeid() operator

是的,使用typeid()运算符

For example:

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

Output:

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

If the type typeid evaluates is a pointer preceded by the dereference operator (*), and this pointer has a null value, typeid throws a bad_typeid exception

如果类型typeid求值是一个以解引用运算符(*)开头的指针,并且该指针具有空值,则typeid会抛出bad_typeid异常

Read more.....

#5


1  

You can use e.g. typeid to query an object's dynamic type, but I don't know of a way to directly instantiate a new object from the type information.

你可以用例如用于查询对象的动态类型的typeid,但我不知道从类型信息直接实例化新对象的方法。

However, apart from the clone approach mentioned above, you could use a factory:

但是,除了上面提到的克隆方法,您可以使用工厂:

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

P.S.: The essential part of this code example is of course the Factory::createFrom method. (It's probably not the most beautiful C++ code, since my C++ has gone a little rusty. The factory method probably shouldn't be static, on second thought.)

P.S。:这个代码示例的基本部分当然是Factory :: createFrom方法。 (它可能不是最漂亮的C ++代码,因为我的C ++有点生疏。工厂方法可能不应该是静态的,再考虑一下。)

#6


1  

I used macros in my project to synthesize such methods. I'm just researching this approach now, so I may be wrong, but here's an answer to your question in my code of IAllocable.hh. Note that I use GCC 4.8, but I hope 4.7 suits.

我在我的项目中使用宏来合成这样的方法。我现在正在研究这种方法,所以我可能错了,但这是我在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;
};

Usage:

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

Hope it helps.

希望能帮助到你。

#7


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

#8


0  

When there are extremely many classes deriving from the same base class then this code will save you from having to include clone methods every class. It's a more convenient way of cloning that involves templates and an intermediate subclass. It's doable if the hierarchy is shallow enough.

当从同一个基类派生的类非常多时,这个代码将使您不必每个类都包含克隆方法。这是一种更方便的克隆方法,涉及模板和中间子类。如果层次结构足够浅,那么它是可行的。

struct PureBase {
    virtual Base* Clone() {
        return nullptr;
    };
};

template<typename T>
struct Base : PureBase {
    virtual Base* Clone() {
        return new T();
    }
};

struct Derived : Base<Derived> {};

int main() {
    PureBase* a = new Derived();
    PureBase* b = a->Clone(); // typeid(*b) == typeid(Derived)
}

#1


12  

Clone method

There is nothing provided by the language that queries type and lets you construct from that information, but you can provide the capability for your class heirarchy in various ways, the easiest of which is to use a virtual method:

查询类型的语言没有提供任何内容,并允许您从该信息构造,但您可以通过各种方式为类层次结构提供功能,最简单的方法是使用虚拟方法:

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

This does something slightly different: clone the current object. This is often what you want, and allows you to keep objects around as templates, which you then clone and modify as desired.

这有点不同:克隆当前对象。这通常是您想要的,并允许您将对象保留为模板,然后根据需要进行克隆和修改。

Expanding on Tronic, you can even generate the clone function.

扩展Tronic,您甚至可以生成克隆功能。

Why auto_ptr? So you can use new to allocate the object, make the transfer of ownership explicit, and the caller has no doubt that delete must deallocate it. For example:

为什么选择auto_ptr?因此,您可以使用new来分配对象,使所有权转移显式,并且调用者毫无疑问删除必须释放它。例如:

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

Object factory

If you'd prefer to do exactly as you asked and get a factory that can be used independently of instances:

如果你更愿意完全按照你的要求去做一个可以独立于实例使用的工厂:

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

Much of the same logic and functionality can be used as for a clone method, as get_factory fulfills half of the same role, and the return type (and its meaning) is the only difference.

许多相同的逻辑和功能可以用作克隆方法,因为get_factory实现了相同角色的一半,并且返回类型(及其含义)是唯一的区别。

I've also covered factories a couple times already. You could adapt my SimpleFactory class so your factory object (returned by get_factory) held a reference to a global factory plus the parameters to pass to create (e.g. the class's registered name—consider how to apply boost::function and boost::bind to make this easy to use).

我已经几次覆盖了工厂。您可以调整我的SimpleFactory类,以便您的工厂对象(由get_factory返回)保存对全局工厂的引用以及要传递给create的参数(例如,类的注册名称 - 考虑如何将boost :: function和boost :: bind应用于使这个易于使用)。

#2


6  

The commonly used way to create copies of objects by base class is adding a clone method, which is essentially a polymorphic copy constructor. This virtual function normally needs to be defined in every derived class, but you can avoid some copy&paste by using the 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;
}

Notice that you can use the same CRTP class for any functionality that would be the same in every derived class but that requires knowledge of the derived type. There are many other uses for this besides clone(), e.g. double dispatch.

请注意,您可以将相同的CRTP类用于在每个派生类中相同但需要了解派生类型的任何功能。除了clone()之外,还有许多其他用途。双重调度。

#3


3  

There's only some hacky ways to do this.

只有一些黑客的方法来做到这一点。

The first and IMHO the ugliest is:

第一个和恕我直言最丑陋的是:

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

Note that this will only work if you have RTTI enabled and Base contains some virtual function.

请注意,这仅在启用了RTTI且Base包含某些虚拟功能时才有效。

The second an neater version is to add a pure virtual clone function to the base class

第二个更简洁的版本是向基类添加纯虚拟克隆功能

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

This is much neater.

这个更整洁。

One cool/interesting thing about this is that Foo::clone returns a Foo*, while Bar::clone returns a Bar*. You might expect this to break things, but everything works due to a feature of C++ called covariant return types.

一个很酷/有趣的事情是Foo :: clone返回一个Foo *,而Bar :: clone返回一个Bar *。你可能希望这会破坏一些东西,但是由于C ++的一个叫做协变返回类型的特性,一切都可以运行。

Unfortunately covariant return types don't work for smart pointers, so using sharted_ptrs your code would look like this.

不幸的是,协变返回类型不适用于智能指针,所以使用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();
}

#4


1  

In C++, is there any way to query the type of an object...

在C ++中,有没有办法查询对象的类型...

Yes, use typeid() operator

是的,使用typeid()运算符

For example:

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

Output:

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

If the type typeid evaluates is a pointer preceded by the dereference operator (*), and this pointer has a null value, typeid throws a bad_typeid exception

如果类型typeid求值是一个以解引用运算符(*)开头的指针,并且该指针具有空值,则typeid会抛出bad_typeid异常

Read more.....

#5


1  

You can use e.g. typeid to query an object's dynamic type, but I don't know of a way to directly instantiate a new object from the type information.

你可以用例如用于查询对象的动态类型的typeid,但我不知道从类型信息直接实例化新对象的方法。

However, apart from the clone approach mentioned above, you could use a factory:

但是,除了上面提到的克隆方法,您可以使用工厂:

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

P.S.: The essential part of this code example is of course the Factory::createFrom method. (It's probably not the most beautiful C++ code, since my C++ has gone a little rusty. The factory method probably shouldn't be static, on second thought.)

P.S。:这个代码示例的基本部分当然是Factory :: createFrom方法。 (它可能不是最漂亮的C ++代码,因为我的C ++有点生疏。工厂方法可能不应该是静态的,再考虑一下。)

#6


1  

I used macros in my project to synthesize such methods. I'm just researching this approach now, so I may be wrong, but here's an answer to your question in my code of IAllocable.hh. Note that I use GCC 4.8, but I hope 4.7 suits.

我在我的项目中使用宏来合成这样的方法。我现在正在研究这种方法,所以我可能错了,但这是我在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;
};

Usage:

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

Hope it helps.

希望能帮助到你。

#7


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

#8


0  

When there are extremely many classes deriving from the same base class then this code will save you from having to include clone methods every class. It's a more convenient way of cloning that involves templates and an intermediate subclass. It's doable if the hierarchy is shallow enough.

当从同一个基类派生的类非常多时,这个代码将使您不必每个类都包含克隆方法。这是一种更方便的克隆方法,涉及模板和中间子类。如果层次结构足够浅,那么它是可行的。

struct PureBase {
    virtual Base* Clone() {
        return nullptr;
    };
};

template<typename T>
struct Base : PureBase {
    virtual Base* Clone() {
        return new T();
    }
};

struct Derived : Base<Derived> {};

int main() {
    PureBase* a = new Derived();
    PureBase* b = a->Clone(); // typeid(*b) == typeid(Derived)
}