在C ++中,有没有办法查询对象的类型,然后使用该信息动态创建相同类型的新对象?
例如,假设我有一个简单的3类层次结构:
class Base
class Foo : public Base
class Bar : public Base
现在假设我给你一个类型为Base的对象 - 实际上是Foo类型。 有没有办法查询类型并使用该信息以便以后创建Foo类型的新对象?
答案 0 :(得分:12)
查询类型的语言没有提供任何内容,并允许您从该信息构造,但您可以通过各种方式为类层次结构提供功能,最简单的方法是使用虚拟方法:
struct Base {
virtual ~Base();
virtual std::auto_ptr<Base> clone(/*desired parameters, if any*/) const = 0;
};
这有点不同:克隆当前对象。这通常是您想要的,并允许您将对象保留为模板,然后根据需要进行克隆和修改。
在Tronic上展开,您甚至可以generate clone function。
为什么 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 实现了同一角色的一半,并且返回类型(及其含义)是唯一的区别。 / p>
我已经在couple times工厂了。您可以调整我的SimpleFactory class,以便您的工厂对象(由 get_factory 返回)保存对全局工厂的引用以及要传递给创建的参数(例如,类的注册名称 - 考虑如何申请 boost :: function 和 boost :: bind 以使其易于使用。)
答案 1 :(得分:6)
通过基类创建对象副本的常用方法是添加克隆方法,该方法本质上是一个多态复制构造函数。这个虚函数通常需要在每个派生类中定义,但是你可以使用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()之外,还有许多其他用途。双重派遣。
答案 2 :(得分:3)
只有一些黑客的方法可以做到这一点。
第一个和最丑陋的恕我直言:
Base * newObjectOfSameType( Base * b )
{
if( dynamic_cast<Foo*>( b ) ) return new Foo;
if( dynamic_cast<Bar*>( b ) ) return new Bar;
}
请注意,这仅在您启用了RTTI且Base包含一些虚拟功能时才有效。
第二个更简洁的版本是将纯虚拟克隆功能添加到基类
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
代码看起来就像这样。
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();
}
答案 3 :(得分:1)
在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求值是一个以取消引用运算符(*)开头的指针,并且该指针具有空值,则typeid会抛出 bad_typeid异常
答案 4 :(得分: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;
}
P.S。:此代码示例的基本部分当然是Factory::createFrom
方法。 (它可能不是最漂亮的C ++代码,因为我的C ++有点生疏。工厂方法可能不应该是静态的,再考虑一下。)
答案 5 :(得分: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;
}
}
希望它有所帮助。
答案 6 :(得分: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);
}