如何设计基类,使其在运行时知道所有"derived"类?
How can one design a base class, so it knows about all the "derived" classes, at run time?
通常,如果您事先知道要创建的所有类型,您可以执行以下操作:
typedef enum{
BASE_CREATURE_TYPE = 0,
ANIMAL_CREATURE_TYPE,
...
}CREATURE_TYPES
但这会变得乏味,因为每次创建新类时,都需要更新枚举。此外,CREATEURE_TYPES仍然只是枚举中的项-如何绑定到实际的类?
我想知道是否有什么方法,我可以只写类,在运行时,在不实例化对象的情况下,创建一个包含所有类型的集合。
这在C++中可能吗?在Java中,有一种叫做"静态块"的东西,它在JVM加载类时执行。
编辑:这个问题不是关于静态块的-这只是一个例子-我想知道是否有办法,我可以执行一个方法或代码块,这样我就知道运行时存在什么类,而不需要实际创建对象
编辑:我指的是所有类型的集合,而不是"映射",所以我可以创建每种类型的对象,而不必维护列表。
EDIT:我之所以想要这个,是因为我正在尝试创建一个函数,该函数可以调用应用程序中所有派生类的方法。例如,假设我有几个类,它们都派生自类Foo,并且有一个balls():
Foo{
balls();
}
Boo : public Foo{
balls();
}
Coo: public Foo{
balls():
}
在运行时,我想了解所有派生类,这样我就可以调用:
DerivedClass:balls();
编辑:注意,我不需要知道每个派生类的所有成员,我只想知道所有派生类是什么,所以我可以在每个派生类上调用balls()。
编辑:这个问题类似:如何在创建时自动注册类
但不幸的是,他正在存储一个std::string()。如何引用实际的类?
编辑:在Smeehey下面的回答中,在main方法中,我将如何实际创建每个类的实例,并调用静态和非静态方法?
您可以为所有类创建一个静态注册表,并使用几个辅助宏在其中注册新类型。下面是一个基本的工作演示,它从Base创建了两个派生类。要添加新类,只需使用显示的两个宏——一个在类内,一个在外。注意:这个例子非常简单,不涉及检查重复项或其他错误条件,以最大限度地提高清晰度。
class BaseClass
{
};
class Registry
{
public:
static void registerClass(const std::string& name, BaseClass* prototype)
{
registry[name] = prototype;
}
static const std::map<std::string, BaseClass*>& getRegistry() { return registry; };
private:
static std::map<std::string, BaseClass*> registry;
};
std::map<std::string, BaseClass*> Registry::registry;
#define REGISTER_CLASS(ClassType) static int initProtoType() { static ClassType proto; Registry::registerClass(std::string(#ClassType), &proto); return 0; } static const int regToken;
#define DEFINE_REG_CLASS(ClassType) const int ClassType::regToken = ClassType::initProtoType();
class Instance : public BaseClass
{
REGISTER_CLASS(Instance)
};
DEFINE_REG_CLASS(Instance)
class OtherInstance : public BaseClass
{
REGISTER_CLASS(OtherInstance)
};
DEFINE_REG_CLASS(OtherInstance)
int main()
{
for(auto entry : Registry::getRegistry())
{
std::cout << entry.first << std::endl;
}
return 0;
}
上面注册了派生类的原型,例如可以用于复制构建其他实例。作为一种替代方案,根据OP的要求,您可以拥有一个注册工厂方法而不是原型的系统。这允许您使用具有任何特定签名的构造函数而不是复制构造函数来创建实例:
class BaseClass
{
};
class Registry
{
public:
using factoryMethod = BaseClass* (*)(int a, int b, int c);
static void registerClass(const std::string& name, factoryMethod meth)
{
registry[name] = meth;
}
static BaseClass* createInstance(const std::string& type, int a, int b, int c)
{
return registry[type](a, b, c);
}
static const std::map<std::string, factoryMethod>& getRegistry() { return registry; };
private:
static std::map<std::string, factoryMethod> registry;
};
std::map<std::string, Registry::factoryMethod> Registry::registry;
#define REGISTER_CLASS(ClassType) static BaseClass* createInstance(int a, int b, int c)
{
return new ClassType(a,b,c);
}
static int initRegistry()
{
Registry::registerClass(
std::string(#ClassType),
ClassType::createInstance);
return 0;
}
static const int regToken;
#define DEFINE_REG_CLASS(ClassType) const int ClassType::regToken = ClassType::initRegistry();
class Instance : public BaseClass
{
Instance(int a, int b, int c){}
REGISTER_CLASS(Instance)
};
DEFINE_REG_CLASS(Instance)
class OtherInstance : public BaseClass
{
OtherInstance(int a, int b, int c){}
REGISTER_CLASS(OtherInstance)
};
DEFINE_REG_CLASS(OtherInstance)
int main()
{
std::vector<BaseClass*> objects;
for(auto entry : Registry::getRegistry())
{
std::cout << entry.first << std::endl;
objects.push_back(Registry::createInstance(entry.first, 1, 2, 3));
}
return 0;
}
使用具有通用"祖先"接口的CRTP设计:
#include <vector>
#include <iostream>
/* Base */
struct IBase
{
virtual void balls() = 0;
virtual IBase *clone() const = 0;
private:
static std::vector<IBase const *> _Derived;
public:
static void
create_all(void)
{
std::cout << "size: " << _Derived.size() << "n";
for (IBase const *a : _Derived)
{
IBase *new_object(a->clone());
(void)new_object; // do something with it
}
}
};
std::vector<IBase const *> IBase::_Derived;
/* Template for CRTP */
template<class DERIVED>
class Base : public IBase
{
static bool created;
static Base const *_model;
public:
Base(void)
{
if (not created)
{
_Derived.push_back(this);
created = true;
}
}
};
template<class DERIVED>
bool Base<DERIVED>::created = false;
template<class DERIVED>
Base<DERIVED> const *Base<DERIVED>::_model = new DERIVED;
/* Specialized classes */
struct Foo1 : public Base<Foo1>
{
IBase *clone() const
{
std::cout << "new Foo1n";
return new Foo1(*this);
}
void balls() {}
};
struct Foo2 : public Base<Foo2>
{
IBase *clone() const
{
std::cout << "new Foo2n";
return new Foo2(*this);
}
void balls() {}
};
int main(void)
{
Foo1 a;
IBase::create_all();
}
我尝试了这个解决方案,但我不知道为什么在运行程序时没有创建static Base const *_model;
。
您可以使用全局工厂来保存能够创建派生类的对象(unique_ptr)的函数:
#include <memory>
#include <unordered_map>
#include <typeinfo>
#include <typeindex>
// Factory
// =======
template <typename Base>
class Factory
{
public:
template <typename Derived>
struct Initializer {
Initializer() {
Factory::instance().register_producer<Derived>();
}
};
typedef std::function<std::unique_ptr<Base>()> producer_function;
typedef std::unordered_map<std::type_index, producer_function> producer_functions;
static Factory& instance();
void register_producer(const std::type_info& type, producer_function producer) {
m_producers[std::type_index(type)] = std::move(producer);
}
template <typename Derived>
void register_producer() {
register_producer(
typeid(Derived),
[] () { return std::make_unique<Derived>(); });
}
producer_function producer(const std::type_info& type) const {
auto kv = m_producers.find(std::type_index(type));
if(kv != m_producers.end())
return kv->second;
return producer_function();
}
const producer_functions producers() const { return m_producers; }
private:
producer_functions m_producers;
};
template <typename Base>
Factory<Base>& Factory<Base>::instance() {
static Factory result;
return result;
}
// Test
// ====
#include <iostream>
class Base
{
public:
~Base() {}
virtual void print() = 0;
};
class A : public Base
{
public:
void print() override { std::cout << "An"; }
static void f() {}
};
Factory<Base>::Initializer<A> A_initializer;
class B : public Base
{
public:
void print() override { std::cout << "Bn"; }
};
Factory<Base>::Initializer<B> B_initializer;
class C {};
int main()
{
auto& factory = Factory<Base>::instance();
// unique_ptr
auto producerA = factory.producer(typeid(A));
if(producerA) {
auto ptrA = producerA();
ptrA->print();
}
// shared_ptr
auto producerB = factory.producer(typeid(B));
if(producerB) {
std::shared_ptr<Base> ptrB(producerB());
ptrB->print();
}
// missing
auto producerC = factory.producer(typeid(C));
if( ! producerC) {
std::cout << "No producer for Cn";
}
// unordered
for(const auto& kv : factory.producers()) {
kv.second()->print();
}
}
注意:工厂不提供在没有对象的情况下调用静态成员函数的方法。
- CMake-按正确顺序将项目与C运行时对象文件链接
- 我在c++代码中生成了一个运行时#3异常
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 删除指向指针的指针是运行时错误吗
- 如何用参数值调用函数(仅在运行时已知)
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- c++中的指针和运行时错误
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 建议在运行时将带有类实例的列表从c++导入qml
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 读取文件时运行时的未知行为
- 函数在Windows或Linux上运行时表现不同
- 在声明中合并两个常量"std::set"(不是在运行时)
- AWS Lambda C++运行时权限被拒绝
- 通过switch和static_cast访问多态对象的运行时类型
- 如何设计基类,使其在运行时知道所有"derived"类?