关于c ++:从动态类型信息创建新对象

Creating a new object from dynamic type info

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

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

1
2
3
class Base
class Foo : public Base
class Bar : public Base

现在假设我给您一个对象类型转换为类型库——实际上是foo类型。是否有方法查询类型并使用该信息稍后创建foo类型的新对象?


克隆法

查询类型的语言没有提供任何内容,并且允许您根据该信息构造,但是您可以以各种方式为类继承权提供功能,其中最简单的方法是使用虚拟方法:

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

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

在tronic上扩展,您甚至可以生成克隆功能。

为什么AutoTPTR?因此,您可以使用new来分配对象,使所有权的转移显式化,调用方毫无疑问,delete必须取消分配它。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}

对象工厂

如果您希望按照您的要求做,并得到一个可以独立于实例使用的工厂:

1
2
3
4
5
6
7
8
9
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返回)就拥有了对全局工厂的引用以及要传递给创建的参数(例如,该类的注册名称&mdash;请考虑如何应用boost::function和boost::bind以使其易于使用)。


通过基类创建对象副本的常用方法是添加一个克隆方法,该方法本质上是一个多态复制构造函数。通常需要在每个派生类中定义此虚拟函数,但可以使用奇怪的重复模板模式来避免某些复制和粘贴:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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()之外,还有许多其他用途,例如双调度。


只有一些下流的方法可以做到这一点。

最丑的是:

1
2
3
4
5
Base * newObjectOfSameType( Base * b )
{
  if( dynamic_cast<Foo*>( b ) ) return new Foo;
  if( dynamic_cast<Bar*>( b ) ) return new Bar;
}

请注意,只有在启用了rtti并且base包含一些虚拟函数的情况下,这才有效。

第二个更整洁的版本是向基类添加纯虚拟克隆函数

1
2
3
4
5
6
7
8
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();
}

这里整洁多了。

有件很酷/有趣的事是Foo::clone返回Foo*Bar::clone返回Bar*。你可能会认为这会破坏事情,但是一切都是因为C++的一个特性,叫做协变返回类型。

不幸的是,协变返回类型不适用于智能指针,因此使用sharted_ptrs您的代码将如下所示。

1
2
3
4
5
6
7
8
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();
}


我在项目中使用宏来合成这些方法。我现在正在研究这种方法,所以我可能是错的,但我的代码iallocable.hh中有一个问题的答案。请注意,我使用GCC4.8,但我希望4.7适合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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;
};

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Usage : public virtual IAllocable {

    SYNTHESIZE_I_ALLOCABLE

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

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

希望它有帮助。


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

但是,除了上述clone方法外,您还可以使用工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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.:这个代码示例的基本部分当然是Factory::createFrom方法。(这可能不是最漂亮的C++代码,因为我的C++已经有点生锈了。再想一想,工厂方法可能不应该是静态的。)


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

是的,使用typeid()运算符

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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() << '
'
;
     cout <<"b is:" << typeid(b).name() << '
'
;
     cout <<"*a is:" << typeid(*a).name() << '
'
;
     cout <<"*b is:" << typeid(*b).name() << '
'
;
    } catch (exception& e) { cout <<"Exception:" << e.what() << endl; }
    return 0;
  }

输出:

1
2
3
4
a is: class CBase *
b is: class CBase *
*a is: class CBase
*b is: class CDerived

如果类型typeid的计算结果是一个前面有取消引用运算符(*)的指针,并且此指针的值为空,则typeid将引发一个错误的_typeid异常

多读…


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
}