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(); } |
这里整洁多了。
有件很酷/有趣的事是
不幸的是,协变返回类型不适用于智能指针,因此使用
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; } } |
希望它有帮助。
您可以使用例如
但是,除了上述
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.:这个代码示例的基本部分当然是
In C++, is there any way to query the type of an object...
是的,使用
例如:
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); } |