假设我们有一个基类和一堆派生类。有没有办法或机制以编程方式知道所有派生类名?
也许反射是一个好主意,但它不适用于C ++。我想在编译期间会有某种模板可以完成这项工作。
class Base{
public:
virtual void print(){
// This function should print all the names of derived class.
}
virtual Base* getInstance(string class_name){
// This function should return an instance related to the class name.
}
};
class Derived_1 : public Base{ // Suppose we have 100 Derived_X classes,
// so we don't want to add its name to a list manually.
};
int main(){
Base base;
base.print(); // This should print the name of all the derived class.
base.getInstance("Derived_1"); // This should return an instance of Derived_1
return 0;
}
答案 0 :(得分:1)
对于getInstance
,您可以将其声明为模板(需要C ++ 14)。要获得程序中派生类的所有名称,您可能不得不求助于某些预处理程序。
#include <type_traits>
class Base
{
public:
virtual ~Base () = default;
template < typename T,
typename = std::enable_if_t<std::is_base_of<Base, T>::value, void>
>
T getInstance() { return T{}; }
};
class Derived : public Base {};
class NotDerived {};
int main(){
Base base;
base.getInstance<Derived>();
// error: no matching member function for call to 'getInstance'
//base.getInstance<NotDerived>();
}
答案 1 :(得分:0)
关于派生类的名称,我提出了一个基于BaseList
类/结构的解决方案,其中包含静态std::set
(或其他容器)名称,模板Base
class / struct,继承自BaseList
并且其模板参数是派生类(CRTP样式)和(为了简化派生类/结构的构造,一个C风格的宏(我知道......宏是蒸馏的邪恶...但有时......)用必要的静态方法创建派生类/结构的声明,声明派生类/结构的名称,以及成员(激活名称的注册)
以下是一个完整的示例(遗憾的是C ++ 11)
#include <set>
#include <string>
#include <iostream>
struct BaseList
{
static std::set<std::string> const & derList (std::string const & dn)
{
static std::set<std::string> dl;
if ( dn.size() )
dl.insert(dn);
return dl;
}
static void print ()
{
std::cout << "derived names: ";
for ( auto const & dn : derList("") )
std::cout << dn << ", ";
std::cout << std::endl;
}
};
template <typename Der>
struct Base : public BaseList
{
static std::size_t setNameInList ()
{ return derList(Der::name()).size(); }
static std::size_t id;
};
template <typename Der>
std::size_t Base<Der>::id = setNameInList();
#define setDerived(nameDer) \
struct nameDer : public Base<nameDer>\
{ \
std::size_t idc { id }; \
static std::string name () \
{ return #nameDer; }
setDerived(Derived_1)
// other elements
};
setDerived(Derived_2)
// other elements
};
setDerived(Derived_3)
// other elements
};
int main()
{
BaseList::print();
}
关于getInstance()
问题,我能想象的唯一解决方案是Enry Menke(+1)提出的相同解决方案,所以我建议您通过模板类型参数获取实例。
答案 2 :(得分:0)
此解决方案基于以下事实:您实际上正在寻找工厂。它使用一个小宏来简化课程注册,希望你不要关心它。
<强> factory.h 强>
#ifndef __FACTORY_H__
#define __FACTORY_H__
#include <map>
#include <functional>
#include <string>
#include <iostream>
template<class B>
class Factory {
static std::map<std::string, std::function<B*()>> s_creators;
public:
template<class T>
static void registerClass(const std::string& name) {
s_creators[name] = []() { return new T(); };
}
static B* create(const std::string& name) {
const auto it = s_creators.find(name);
if (it == s_creators.end()) return nullptr; // not a derived class
return (it->second)();
}
static void printRegisteredClasses() {
for (auto it = s_creators.begin(); it != s_creators.end(); ++it)
std::cout << it->first << std::endl;
}
};
template<class B> std::map<std::string, std::function<B*()>> Factory<B>::s_creators;
template<class B, class T>
class Creator {
public:
Creator(const std::string& name) {
Factory<B>::template registerClass<T>(name);
}
};
#define REGISTER(base_class, derived_class) \
Creator<base_class, derived_class> s_##derived_class##Creator(#derived_class);
#endif
<强> example.cpp 强>
#include "factory.h"
#include <memory>
class Base {
public:
virtual void printName() const { std::cout << "Base" << std::endl; }
};
class Derived1 : public Base {
public:
virtual void printName() const override { std::cout << "Derived1" << std::endl; }
};
REGISTER(Base, Derived1);
class Derived2 : public Base {
public:
virtual void printName() const override { std::cout << "Derived2" << std::endl; }
};
REGISTER(Base, Derived2);
int main() {
std::cout << "Registered classes:" << std::endl;
Factory<Base>::printRegisteredClasses();
std::cout << "---" << std::endl;
std::unique_ptr<Base> derived1(Factory<Base>::create("Derived1"));
derived1->printName();
return 0;
}
Notes :需要C ++ 11。我已经使用clang 3.9.0进行了测试,我还没有机会与其他编译器进行验证。