Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.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++ 我如何使我的工厂';s标头是否不依赖于它创建的模板化对象?_C++_Templates_Design Patterns_Dependencies_Factory - Fatal编程技术网

C++ 我如何使我的工厂';s标头是否不依赖于它创建的模板化对象?

C++ 我如何使我的工厂';s标头是否不依赖于它创建的模板化对象?,c++,templates,design-patterns,dependencies,factory,C++,Templates,Design Patterns,Dependencies,Factory,我有这样一个抽象基类: class AbstractBaseClass {}; 从中派生的模板化具体类: template<class T> class ConcreteClass : public AbstractBaseClass { public: ConcreteClass(T input) : data(input) {} private: T data; }; 模板 类ConcreteClass:公共抽象基类 { 公众: 具体类(T输入):数据(输入)

我有这样一个抽象基类:

class AbstractBaseClass
{}; 
从中派生的模板化具体类:

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};
模板
类ConcreteClass:公共抽象基类
{
公众:
具体类(T输入):数据(输入){
私人:
T数据;
};
我有一个创建抽象基类的工厂类

class MyFactory
{
public:
   boost::shared_ptr<AbstractBaseClass> CreateBlah();
   boost::shared_ptr<AbstractBaseClass> CreateFoo();

   template<class T>
   boost::shared_ptr<AbstractBaseClass> Create(T input)
   {
      return boost::shared_ptr<AbstractBaseClass>(new ConcreteClass<T>(input));
   }
};
类MyFactory
{
公众:
boost::shared_ptr CreateBlah();
boost::shared_ptr CreateFoo();
模板
boost::共享的ptr创建(T输入)
{
返回boost::shared_ptr(新的ConcreteClass(输入));
}
};
问题是,现在所有使用MyFactory的东西都必须包含整个实现到ConcreteClass。理想情况下,我只想让我的工厂知道这个类


是否有任何方法来构建此架构以实现此目标?(除了在MyFactory中为我想要的每种类型手动创建一个新的Create函数,而不是模板化它)。

您正在寻找“PIMPL”习惯用法。这里有一个很好的解释,因为ConcreteClass是一个模板,所以不能这样做,这意味着您需要在编译时提供完整的实现。这也是为什么您不能预编译模板,而必须将它们全部写入头文件的原因。

您需要将工厂实现放入实现文件中(您提到过,您希望避免这种情况,但除非接口很小和/或您的项目很小,否则它的危害较小)

当然,还有其他一些方法可以实现这一点,比如将实现放入基类中,创建派生的基类工厂,或者使用其他一些非常奇怪的模板语法来减少依赖翻译中的实例化。这实际上取决于项目的便利性和规模。如果您正在处理一个或多个大型项目,那么从长远来看,完整的抽象wrt实例化将最好地满足您的需求(假设您需要动态多态性和内存)

您还可以尝试其他方法(例如重载),通过使用类型安全性来减少错误

简单的回答是,您确实需要将接口/实例化抽象为一个或多个实现文件,以删除头依赖项—这是一个非常常见的习惯用法,并且有许多方法可以解决它。当然,您还可以为工厂进一步划分和使用多态性

您还可以使用模板转发声明来最小化到编译单元的集合。提供:

/** in MyIntermediateFactory.hpp */
class MyIntermediateFactory {
public:
    static template<class T> boost::shared_ptr<T> Create(float);
};

/** in Concrete.hpp */
template<Concrete>
boost::shared_ptr<T> MyIntermediateFactory::Create<Concrete>(float arg) {
    /* … */
}
MyIntermediateFactory.hpp中的
/***/
类属性{
公众:
静态模板boost::共享的ptr创建(float);
};
/**在混凝土中。hpp*/
模板
boost::shared_ptr mymediatefactory::Create(float arg){
/* … */
}
使用它,您可以在库中选择所需的部分程序/接口,然后将其封装在一个真正的工厂中(用于手头的构建)。如果您实际尝试请求一个不可见的创建,那么链接器/实例化应该会失败


有很多选择,真的-你需要弄清楚你的规模有多大,以确定要抽象(或不抽象)什么。实例化需要接口,要删除头依赖项,您必须将实例化抽象到某个地方。

您可以使用显式模板实例化。尝试使用未显式实例化的模板参数调用factory方法将导致链接器错误。请注意MyFactory.cpp中的显式模板实例

template AbstractBaseClass* MyFactory::Create(int input);
所有这些放在一起看起来是这样的(为了简单起见,我删除了shared\u ptr):

Main.cpp:

#include "AbstractBaseClass.h"
#include "MyFactory.h"

//we do not need to know nothing about concreteclass (neither MyFactory.h includes it)    

int main()
{
    MyFactory f;
    AbstractBaseClass* ab = f.Create(10);
    ab = f.Create(10.0f);
    return 0;
}
MyFactory.h:

#include "AbstractBaseClass.h"

class MyFactory
{
public:

   template<class T>
   AbstractBaseClass* Create(T input);
 };
混凝土等级h:

#include "AbstractBaseClass.h"

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};
#包括“AbstractBaseClass.h”
模板
类ConcreteClass:公共抽象基类
{
公众:
具体类(T输入):数据(输入){
私人:
T数据;
};

我过去处理同一问题的方法是创建一组具体工厂(每种类型一个),在全局工厂中注册(为了便于说明,按对象名称索引):

class抽象基类;
二级混凝土厂
{
公众:
AbstractBaseClass*创建();
};
类抽象工厂
{
公众:
void registerFactory(std::string const&name,std::shared\u ptr const&f)
{
factory[name]=f;//检查是否存在冲突,如果有,请投诉。。。
}
AbstractBaseClass*创建(标准::字符串常量和名称)
{
返回工厂[name]->create();//在取消引用之前检查是否存在。。。
}
私人:
地图工厂;
};

我在一段代码中使用了它,这段代码经过了大量模板化,以减少编译时间。每个具体工厂及其创建的类只需要位于注册具体工厂的单个翻译单元中。其余的代码只需要使用公共接口来
AbstractBaseClass

,我意识到我在五年后回答了这个问题。也许从那时起,这种语言已经发展了一点。如果我正确理解了这个问题,我想提供一些似乎正确的东西,如果没有别的意义,那就是帮助那些可能发现这个问题并想知道他们能做些什么的人


factory.hpp


这将使用模板、函数重载和通过枚举进行标记的组合来帮助创建一个灵活的factory类,该类不需要对其实例化的各个类了解太多,从而按照OP的要求包含模板化的具体类

“ederive”标记(以枚举的形式)告诉编译器使用工厂的create函数的版本,该版本采用类似template_派生类的类,该类具有允许我将数据分配给其成员之一的函数。从我在main.cpp中排序标题的方式可以看出,工厂对模板不了解。调用基类的虚函数的函数也不做任何事情。我想这就是OP的意思
class AbstractBaseClass{};
#include "AbstractBaseClass.h"

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};
class AbstractBaseClass;
class ConcreteFactory
{
public:
   AbstractBaseClass * create();
};
class AbstractFactory 
{
public:
   void registerFactory( std::string const & name, std::shared_ptr<ConcreteFactory> const & f )
   {
      factory[ name ] = f; // check for collisions, complain if so ...
   }
   AbstractBaseClass * create( std::string const & name )
   {
      return factory[name]->create(); // check for existence before dereferencing...
   }
private:
   std::map<std::string, std::shared_ptr<ConcreteFactory> > factory;
};
#include "base.hpp"

namespace tvr
{
    namespace test
    {
        class factory
        {
        public:
            typedef base::ptr Ptr;

            enum eSpecial
            {
                eDerived
            };

            template<typename Type>
            Ptr create()
            {
                Ptr result;
                result.reset(new Type());
                return result;
            }

            template<typename Type, typename DataType>
            Ptr create(const DataType& data)
            {
                Ptr result;
                result.reset(new Type(data));
                return result;
            }

            template<typename Type, typename DataType>
            Ptr create(const DataType& data, eSpecial tag)
            {
                Ptr result;
                result.reset(new Type());
                static_cast<Type*>(result.get())->set_item(data);
                return result;
            }
        };
    }
}
#include <memory>

namespace tvr
{
    namespace test
    {
        class base
        {
        public:
            typedef std::shared_ptr<base> ptr;

        public:
            base() {}
            virtual ~base() {}
            virtual void do_something() = 0;
        };
    }
}
#include <ostream>

namespace tvr
{
    namespace test
    {
        struct some_class
        {
        };
    }
}

std::ostream& operator<<(std::ostream& out, const tvr::test::some_class& item)
{
    out << "This is just some class.";
    return out;
}
#include <iostream>

#include "base.hpp"

namespace tvr
{
    namespace test
    {
        template<typename Type>
        class template_derived : public base
        {
        public:
            template_derived(){}
            virtual ~template_derived(){}
            virtual void do_something()
            {
                std::cout << "Doing something, like printing _item as \"" << _item << "\"." << std::endl;
            }

            void set_item(const Type data)
            {
                _item = data;
            }
        private:
            Type _item;
        };
    }
}
#include <vector>

#include "base.hpp"
#include "factory.hpp"

namespace tvr
{
    namespace test
    {
        typedef std::vector<tvr::test::base::ptr> ptr_collection;

        struct iterate_collection
        {
            void operator()(const ptr_collection& col)
            {
                for (ptr_collection::const_iterator iter = col.begin();
                    iter != col.end();
                    ++iter)
                {
                    iter->get()->do_something();
                }
            }
        };
    }
}

#include "template_derived.hpp"
#include "some_class.hpp"

namespace tvr
{
    namespace test
    {
        inline int test()
        {
            ptr_collection items;

            tvr::test::factory Factory;

            typedef template_derived<unsigned int> UIntConcrete;
            typedef template_derived<double> DoubleConcrete;
            typedef template_derived<std::string> StringConcrete;
            typedef template_derived<some_class> SomeClassConcrete;

            items.push_back(Factory.create<SomeClassConcrete>(some_class(), tvr::test::factory::eDerived));
            for (unsigned int i = 5; i < 7; ++i)
            {
                items.push_back(Factory.create<UIntConcrete>(i, tvr::test::factory::eDerived));
            }
            items.push_back(Factory.create<DoubleConcrete>(4.5, tvr::test::factory::eDerived));
            items.push_back(Factory.create<StringConcrete>(std::string("Hi there!"), tvr::test::factory::eDerived));

            iterate_collection DoThem;
            DoThem(items);

            return 0;
        }
    }
}

int main(int argc, const char* argv[])
{
    tvr::test::test();
}
Doing something, like printing _item as "This is just some class.".
Doing something, like printing _item as "5".
Doing something, like printing _item as "6". 
Doing something, like printing _item as "4.5".
Doing something, like printing _item as "Hi there!".