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

Dan*_*iel 20 c++ rtti

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

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

class Base
class Foo : public Base
class Bar : public Base
Run Code Online (Sandbox Code Playgroud)

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

小智 12

克隆方法

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

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

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

扩展在Tronic公司,你甚至可以产生克隆功能.

为什么选择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();
}
Run Code Online (Sandbox Code Playgroud)

对象工厂

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

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
};
Run Code Online (Sandbox Code Playgroud)

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

我还涵盖工厂一对夫妇 了.您可以调整我的SimpleFactory类,以便您的工厂对象(由get_factory返回)持有对全局工厂的引用以及要传递给create的参数(例如,类的注册名称 - 考虑如何将boost :: functionboost :: bind应用于使这个易于使用).

  • auto_ptr是邪恶的! (3认同)
  • 使用C++创建工厂的一个很好的参考是Andrei Alexandrescu的"现代C++设计". (2认同)

Tro*_*nic 6

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

// 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;
}
Run Code Online (Sandbox Code Playgroud)

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


Pra*_*rav 0

在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;
  }
Run Code Online (Sandbox Code Playgroud)

输出

a is: class CBase *
b is: class CBase *
*a is: class CBase
*b is: class CDerived
Run Code Online (Sandbox Code Playgroud)

如果 typeid 计算的类型是前面带有解引用运算符 (*) 的指针,并且该指针具有空值,则 typeid 会引发bad_typeid 异常

阅读更多.....

  • 这不允许您创建该类型的新对象。 (5认同)
  • 另请注意,“name()”返回的名称是特定于实现的,并且可能不是很有用(尽管您也可以使用特定于实现的分解函数)。 (3认同)
  • 我可以使用 typeid 返回的值来创建该类型的新对象吗?我不想硬编码任何东西。 (2认同)