Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否有一种方法可以从保存其类名的字符串实例化对象?_C++_Inheritance_Factory_Instantiation - Fatal编程技术网

C++ 是否有一种方法可以从保存其类名的字符串实例化对象?

C++ 是否有一种方法可以从保存其类名的字符串实例化对象?,c++,inheritance,factory,instantiation,C++,Inheritance,Factory,Instantiation,我有一个文件:Base.h class Base; class DerivedA : public Base; class DerivedB : public Base; /*etc...*/ 还有另一个文件:BaseFactory.h #include "Base.h" class BaseFactory { public: BaseFactory(const string &sClassName){msClassName = sClassName;}; Base *

我有一个文件:Base.h

class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/
还有另一个文件:BaseFactory.h

#include "Base.h"

class BaseFactory
{
public:
  BaseFactory(const string &sClassName){msClassName = sClassName;};

  Base * Create()
  {
    if(msClassName == "DerivedA")
    {
      return new DerivedA();
    }
    else if(msClassName == "DerivedB")
    {
      return new DerivedB();
    }
    else if(/*etc...*/)
    {
      /*etc...*/
    }
  };
private:
  string msClassName;
};

/*etc.*/
有没有办法将这个字符串转换成实际的类型(类),这样BaseFactory就不必知道所有可能的派生类,并且每个派生类都有if()?我可以从这个字符串生成一个类吗


我认为这可以通过C#反射来实现。C++中有类似的东西吗?

< p>意义反射,如java。 这里有一些信息:


一般来说,在谷歌上搜索“c++反射”

不,没有。我对这个问题的首选解决方案是创建一个字典,将名称映射到创建方法。希望像这样创建的类然后向字典注册一个创建方法。这将在中详细讨论。

这是出厂模式。参见维基百科(和示例)。如果没有一些惊人的技巧,就无法从字符串创建类型本身。你为什么需要这个?

< p>我在另一个关于C++工厂的问题上回答了这个问题。请看是否有兴趣建立一个灵活的工厂。我试图描述一种从ET++到使用宏的老方法,这种方法对我来说非常有效


是将旧Mac App移植到C++和X11的项目。在it的努力中,Eric Gamma等人开始思考设计模式,简单的回答是你不能。请参见以下问题了解原因:


  • 没有,没有,除非你自己做映射。C++没有机制来创建在运行时确定类型的对象。不过,您可以使用地图自己进行映射:

    template<typename T> Base * createInstance() { return new T; }
    
    typedef std::map<std::string, Base*(*)()> map_type;
    
    map_type map;
    map["DerivedA"] = &createInstance<DerivedA>;
    map["DerivedB"] = &createInstance<DerivedB>;
    
    获取一个新实例。另一个想法是让类型自己注册:

    // in base.hpp:
    template<typename T> Base * createT() { return new T; }
    
    struct BaseFactory {
        typedef std::map<std::string, Base*(*)()> map_type;
    
        static Base * createInstance(std::string const& s) {
            map_type::iterator it = getMap()->find(s);
            if(it == getMap()->end())
                return 0;
            return it->second();
        }
    
    protected:
        static map_type * getMap() {
            // never delete'ed. (exist until program termination)
            // because we can't guarantee correct destruction order 
            if(!map) { map = new map_type; } 
            return map; 
        }
    
    private:
        static map_type * map;
    };
    
    template<typename T>
    struct DerivedRegister : BaseFactory { 
        DerivedRegister(std::string const& s) { 
            getMap()->insert(std::make_pair(s, &createT<T>));
        }
    };
    
    // in derivedb.hpp
    class DerivedB {
        ...;
    private:
        static DerivedRegister<DerivedB> reg;
    };
    
    // in derivedb.cpp:
    DerivedRegister<DerivedB> DerivedB::reg("DerivedB");
    
    一个
    boost::variant
    就像一个联合体。它通过查看用于初始化或分配给它的对象来知道存储在其中的类型。看看它的文档。最后,原始函数指针的使用也有点陈旧。现代C++代码应该与特定的函数/类型进行解耦。你可能想寻找更好的方法。它看起来是这样的(地图):

    typedef std::map\u type;
    

    <>代码> STD::函数也将在下一个版本的C++中可用,包括<代码> STD::SyrdYPPTR < /代码> . Tor Brede Vekterli提供了一个增强扩展,它提供了您想要的功能。目前,使用当前的boostlibs有点不方便,但在更改了1.48_0的基本名称空间后,我能够让它使用1.48_0

    回答那些质疑为什么这样的事情(如反射)对C++有用的人——我用它来处理UI和引擎之间的交互——用户在UI中选择一个选项,引擎取UI选择字符串,并产生一个所需类型的对象。 在这里使用框架(而不是在某处维护水果列表)的主要好处是,注册函数位于每个类的定义中(每个注册类只需要一行代码调用注册函数),而不是包含水果列表的文件,每次派生新类时都必须手动将其添加到


    我使工厂成为基类的静态成员。

    boost::functional有一个非常灵活的工厂模板:

    不过,我的首选是生成隐藏映射和对象创建机制的包装器类。我遇到的常见场景是需要将某些基类的不同派生类映射到键,其中派生类都有一个可用的公共构造函数签名。这是我到目前为止提出的解决方案

    #ifndef GENERIC_FACTORY_HPP_INCLUDED
    
    //BOOST_PP_IS_ITERATING is defined when we are iterating over this header file.
    #ifndef BOOST_PP_IS_ITERATING
    
        //Included headers.
        #include <unordered_map>
        #include <functional>
        #include <boost/preprocessor/iteration/iterate.hpp>
        #include <boost/preprocessor/repetition.hpp>
    
        //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated.
        #ifndef GENERIC_FACTORY_MAX_ARITY
            #define GENERIC_FACTORY_MAX_ARITY 10
        #endif
    
        //This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class.
        //Each class generated will have a suffix of the number of parameters taken by the derived type constructors.
        #define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
        #define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY)
        #include BOOST_PP_ITERATE()
    
        #define GENERIC_FACTORY_HPP_INCLUDED
    
    #else
    
        #define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file.
        #define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1))
    
        //This is the class which we are generating multiple times
        template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)>
        class BOOST_PP_CAT(GenericFactory_, N)
        {
            public:
                typedef BasePointerType result_type;
    
            public:
                virtual ~BOOST_PP_CAT(GenericFactory_, N)() {}
    
                //Registers a derived type against a particular key.
                template <class DerivedType>
                void Register(const KeyType& key)
                {
                    m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N));
                }
    
                //Deregisters an existing registration.
                bool Deregister(const KeyType& key)
                {
                    return (m_creatorMap.erase(key) == 1);
                }
    
                //Returns true if the key is registered in this factory, false otherwise.
                bool IsCreatable(const KeyType& key) const
                {
                    return (m_creatorMap.count(key) != 0);
                }
    
                //Creates the derived type associated with key. Throws std::out_of_range if key not found.
                BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const
                {
                    return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a));
                }
    
            private:
                //This method performs the creation of the derived type object on the heap.
                template <class DerivedType>
                BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a))
                {
                    BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a)));
                    return pNewObject;
                }
    
            private:
                typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType;
                typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType;
                CreatorMapType m_creatorMap;
        };
    
        #undef N
        #undef GENERIC_FACTORY_APPEND_PLACEHOLDER
    
    #endif // defined(BOOST_PP_IS_ITERATING)
    #endif // include guard
    

    假定通用工厂头文件名为GenericFactory.hpp

    详细解决方案,用于注册对象并使用字符串名称访问它们

    common.h

    #ifndef COMMON_H_
    #define COMMON_H_
    
    
    #include<iostream>
    #include<string>
    #include<iomanip>
    #include<map>
    
    using namespace std;
    class Base{
    public:
        Base(){cout <<"Base constructor\n";}
        virtual ~Base(){cout <<"Base destructor\n";}
    };
    #endif /* COMMON_H_ */
    
    /*
     * test1.h
     *
     *  Created on: 28-Dec-2015
     *      Author: ravi.prasad
     */
    
    #ifndef TEST1_H_
    #define TEST1_H_
    #include "common.h"
    
    class test1: public Base{
        int m_a;
        int m_b;
    public:
        test1(int a=0, int b=0):m_a(a),m_b(b)
        {
            cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
        }
        virtual ~test1(){cout <<"test1 destructor\n";}
    };
    
    
    
    #endif /* TEST1_H_ */
    
    3. test2.h
    #ifndef TEST2_H_
    #define TEST2_H_
    #include "common.h"
    
    class test2: public Base{
        int m_a;
        int m_b;
    public:
        test2(int a=0, int b=0):m_a(a),m_b(b)
        {
            cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
        }
        virtual ~test2(){cout <<"test2 destructor\n";}
    };
    
    
    #endif /* TEST2_H_ */
    
    main.cpp

    #include "test1.h"
    #include "test2.h"
    
    template<typename T> Base * createInstance(int a, int b) { return new T(a,b); }
    
    typedef std::map<std::string, Base* (*)(int,int)> map_type;
    
    map_type mymap;
    
    int main()
    {
    
        mymap["test1"] = &createInstance<test1>;
        mymap["test2"] = &createInstance<test2>;
    
         /*for (map_type::iterator it=mymap.begin(); it!=mymap.end(); ++it)
            std::cout << it->first << " => " << it->second(10,20) << '\n';*/
    
        Base *b = mymap["test1"](10,20);
        Base *b2 = mymap["test2"](30,40);
    
        return 0;
    }
    

    我需要这个,因为我从文件中读取字符串,如果我有这个,那么我可以让工厂非常通用,它不需要知道任何东西来创建正确的实例。这是非常强大的。那么,你是说你不需要为公共汽车和汽车定义不同的类,因为它们都是汽车吗?但是,如果您这样做,添加另一行应该不是问题:)映射方法也有同样的问题——您更新映射内容。宏thingy适用于普通类。我是说,在我的例子中,为了创建一辆公共汽车或汽车,我不需要不同的定义,否则工厂设计模式将永远不会被使用。我的目标是让工厂尽可能地愚蠢。但是我在这里看到,没有什么可以逃避的:-)您所提到的页面上的内容与标准c++非常不同,我们喜欢派生类将自己注册的想法。这正是我所寻找的,一种从工厂中删除派生类存在的硬编码知识的方法。最初由somedave在另一个问题中发布,该代码在VS2010上失败,由于make_pair导致模棱两可的模板错误。若要修复,请将make_pair更改为std::pair,它应该修复这些错误。我还得到了一些链接错误,通过添加BaseFactory::map_type BaseFactory::map=new map_type()修复了这些错误;到base.cpp如何确保
    DerivedB::reg
    已实际初始化?我的理解是,根据3.6.2.1,如果翻译单元
    derivedb.cpp
    中未定义任何功能或对象,则可能根本无法构建自注册。要编译,我需要一个
    BaseFactory::map\u类型*BaseFactory::map=NULL在我的cpp文件中。没有这个,链接器抱怨未知的符号映射。不幸的是,这不起作用。正如musiphil已经指出的那样,
    DerivedB::reg
    如果在翻译单元
    DerivedB.cpp
    中未定义其任何函数或实例,则不会初始化。这意味着类在实际实例化之前不会被注册。有人知道吗
    #ifndef GENERIC_FACTORY_HPP_INCLUDED
    
    //BOOST_PP_IS_ITERATING is defined when we are iterating over this header file.
    #ifndef BOOST_PP_IS_ITERATING
    
        //Included headers.
        #include <unordered_map>
        #include <functional>
        #include <boost/preprocessor/iteration/iterate.hpp>
        #include <boost/preprocessor/repetition.hpp>
    
        //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated.
        #ifndef GENERIC_FACTORY_MAX_ARITY
            #define GENERIC_FACTORY_MAX_ARITY 10
        #endif
    
        //This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class.
        //Each class generated will have a suffix of the number of parameters taken by the derived type constructors.
        #define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
        #define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY)
        #include BOOST_PP_ITERATE()
    
        #define GENERIC_FACTORY_HPP_INCLUDED
    
    #else
    
        #define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file.
        #define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1))
    
        //This is the class which we are generating multiple times
        template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)>
        class BOOST_PP_CAT(GenericFactory_, N)
        {
            public:
                typedef BasePointerType result_type;
    
            public:
                virtual ~BOOST_PP_CAT(GenericFactory_, N)() {}
    
                //Registers a derived type against a particular key.
                template <class DerivedType>
                void Register(const KeyType& key)
                {
                    m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N));
                }
    
                //Deregisters an existing registration.
                bool Deregister(const KeyType& key)
                {
                    return (m_creatorMap.erase(key) == 1);
                }
    
                //Returns true if the key is registered in this factory, false otherwise.
                bool IsCreatable(const KeyType& key) const
                {
                    return (m_creatorMap.count(key) != 0);
                }
    
                //Creates the derived type associated with key. Throws std::out_of_range if key not found.
                BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const
                {
                    return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a));
                }
    
            private:
                //This method performs the creation of the derived type object on the heap.
                template <class DerivedType>
                BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a))
                {
                    BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a)));
                    return pNewObject;
                }
    
            private:
                typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType;
                typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType;
                CreatorMapType m_creatorMap;
        };
    
        #undef N
        #undef GENERIC_FACTORY_APPEND_PLACEHOLDER
    
    #endif // defined(BOOST_PP_IS_ITERATING)
    #endif // include guard
    
    #include "GenericFactory.hpp"
    
    typedef GenericFactory_3<std::string, std::shared_ptr<BaseClass>, int, int int> factory_type;
    
    factory_type factory;
    factory.Register<DerivedClass1>("DerivedType1");
    factory.Register<DerivedClass2>("DerivedType2");
    factory.Register<DerivedClass3>("DerivedType3");
    
    factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3);
    factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6);
    
    class SomeBaseFactory : public GenericFactory_2<int, BaseType*, std::string, bool>
    {
        public:
            SomeBaseFactory() : GenericFactory_2()
            {
                Register<SomeDerived1>(1);
                Register<SomeDerived2>(2);
            }
    }; 
    
    SomeBaseFactory factory;
    SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true);
    delete someObject;
    
    #define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
    
    #ifndef COMMON_H_
    #define COMMON_H_
    
    
    #include<iostream>
    #include<string>
    #include<iomanip>
    #include<map>
    
    using namespace std;
    class Base{
    public:
        Base(){cout <<"Base constructor\n";}
        virtual ~Base(){cout <<"Base destructor\n";}
    };
    #endif /* COMMON_H_ */
    
    /*
     * test1.h
     *
     *  Created on: 28-Dec-2015
     *      Author: ravi.prasad
     */
    
    #ifndef TEST1_H_
    #define TEST1_H_
    #include "common.h"
    
    class test1: public Base{
        int m_a;
        int m_b;
    public:
        test1(int a=0, int b=0):m_a(a),m_b(b)
        {
            cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
        }
        virtual ~test1(){cout <<"test1 destructor\n";}
    };
    
    
    
    #endif /* TEST1_H_ */
    
    3. test2.h
    #ifndef TEST2_H_
    #define TEST2_H_
    #include "common.h"
    
    class test2: public Base{
        int m_a;
        int m_b;
    public:
        test2(int a=0, int b=0):m_a(a),m_b(b)
        {
            cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
        }
        virtual ~test2(){cout <<"test2 destructor\n";}
    };
    
    
    #endif /* TEST2_H_ */
    
    #include "test1.h"
    #include "test2.h"
    
    template<typename T> Base * createInstance(int a, int b) { return new T(a,b); }
    
    typedef std::map<std::string, Base* (*)(int,int)> map_type;
    
    map_type mymap;
    
    int main()
    {
    
        mymap["test1"] = &createInstance<test1>;
        mymap["test2"] = &createInstance<test2>;
    
         /*for (map_type::iterator it=mymap.begin(); it!=mymap.end(); ++it)
            std::cout << it->first << " => " << it->second(10,20) << '\n';*/
    
        Base *b = mymap["test1"](10,20);
        Base *b2 = mymap["test2"](30,40);
    
        return 0;
    }