Creating a new object from dynamic type info
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?
For example, say I have a simple 3 class hierarchy:
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?
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.
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:
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.
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 (eg the class's registered name—consider how to apply boost::function and boost::bind to make this easy to use).
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(), eg double dispatch.
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.
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.
Unfortunately covariant return types don't work for smart pointers, so using sharted_ptrs
your code would look like this.
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();
}
链接地址: http://www.djcxy.com/p/72900.html
上一篇: C ++ SFINAE的例子?
下一篇: 从动态类型信息创建一个新对象