【问题标题】:C++ createObject() FactoryC++ createObject() 工厂
【发布时间】:2013-01-01 20:03:37
【问题描述】:

我想用简单的 C++ 语法创建一个简单的工厂方法:

void *createObject(const char *str,...)
{
  if(!strcmp("X",str))
     return new X(...);
}

我无法弄清楚这个的语法。我一直在研究模板元编程并使用 mpl::vectors,但我不确定如何传递这种语法。如果可能的话,我想真正避免使用 C va_lists,并采用像上面那样的简洁语法。

【问题讨论】:

  • 由于 Visual Studio 2012 甚至不支持可变参数模板,我正在考虑在编译时使用模板元编程进行实例化,然后以某种方式从 mpl 向量创建某种仿函数。
  • November CTP 增加了对 variadic templates 的支持,请在 aka.ms/vc-ctp 获取

标签: c++ templates boost-mpl


【解决方案1】:

这将是 C++11 上更好的方法:

template< typename ...Args >
std::shared_ptr<void> createObject( std::string const& name, Args&& ...args )
{
    if( name == "X" )
    {
        return try_make_shared< X >( std::forward< Args >( args )... );
    }
    /* other cases here*/

    return nullptr;
}

template< typename T, typename ...Args >
typename std::enable_if<
    std::is_constructible< T, Args >::value
  , std::shared_ptr< T >
>::type try_make_shared( Args&&... args )
{
    return std::make_shared< X >( std::forward< Args >( args )... );
}
template< typename T, typename ...Args >
typename std::enable_if<
    !std::is_constructible< T, Args >::value
  , std::shared_ptr< T >
>::type try_make_shared( Args&&... args )
{
    throw std::invalid_argument( "The type is not constructible from the supplied arguments" );
    return nullptr;
}

与您的代码的区别是

  • 它使用可变参数模板函数而不是省略号参数,因此参数的数量和类型在编译时仍然可用(您不会放松类型检查)。此外,您可以使用 非 POD 类型调用此函数。

  • 它返回一个shared_ptr&lt;void&gt;,而不是一个普通的void*。这使您可以在工厂内控制对象的所有引用消失后应如何清理它。用户不需要知道或关心他是否应该调用标准的delete,或者可能是您工厂的deleteObject 方法。

更新:对于那些建议unique_ptr 的人,您可以阅读here,了解shared_ptr 带来的可能性。一个只返回指向new-ly 分配对象的指针的受限工厂可以并且应该使用unique_ptr

【讨论】:

  • shared_ptr 没有被标准定义,并且它们的实现依赖于平台,如果它们在您的应用程序内部大量使用,它们可能会非常昂贵。我建议使用unique_ptr,除非其他类型的智能指针可以更好地满足您的目标。
  • @user1849534: unique_ptr 在它的删除器上被模板化,所以它不适合这里,一个普通的 void 和一个 deleteObject 成员函数会比这更好。
  • 为什么?你不能指定析构函数/删除函数?我累了,我需要睡觉,但我看不出你的意思;由多个指针拥有的字符串可能非常讨厌。
  • 为了保持一致性,您应该将std:: 添加到make_shared
  • @user805547:有,但是冗长而麻烦。将std::shared_ptr 替换为boost::shared_ptr,并了解在C++11 中如何实现完美转发,其中包含大量重载和boost::[c]ref的用户>
【解决方案2】:

除了如何使用漂亮的 C++11 可变参数模板创建对象(如 K-ballo 的回答中所见)的代码之外,此答案还显示了我将如何处理 a项目中的一组类。这种方法是一个大技巧,只有在你知道自己在做什么的情况下才推荐使用,但是,在向项目添加新类时,你只需要将它们添加到列出所有类的单个文件中,所以如果项目变得很大,这有助于保持概览。

使用这种方法,如果您必须多次列出您的类,例如,如果您还想要一个 std::string className() 函数,例如,返回一个类的名称而不使用C++ 运行时类型信息。需要列出项目中所有类的每个此类功能都可以通过与以下类似的方式实现。

classes.h

/* For every class in your project which should be creatable through your
 * factory, add a line here. */
CLASS(Foo)
CLASS(Bar)
CLASS(Baz)

factory.cpp

template< typename ...Args >
std::shared_ptr<void> createObject( std::string const& name, Args&& ...args )
{
    // Define what code to insert for every class:
#define CLASS(T) \
    else if(name == #T) \
        return std::make_shared<T>(std::forward(args)...);

    // List all cases:
    if(0) /*do nothing*/;  // <-- needed because the macro uses else if
#include "classes.h"
#undef CLASS

    return nullptr;
}

【讨论】:

    【解决方案3】:

    如果您不能使用可变参数模板,并且不想使用 C 风格的可变参数,那么您唯一的选择就是为论据。

    boost::shared_ptr<void> createObject(const char *str,
                                         int argc, const char *argv[])
    {
      if(!strcmp("X",str))
         return new X(argc, argv);
      if(!strcmp("Y",str))
         return make_Y(argc, argv);
    }
    

    Y 所示,将参数处理拆分为工厂函数而不是将构造函数耦合到选项格式可能是明智的。例如,您可能想要切换到属性映射或 Boost 程序选项。

    【讨论】:

      【解决方案4】:

      我最终使用的解决方案是使用模板参数创建 0、N 个单例。它在 N = 8 时工作得很好。有点难看,但只需要做一次。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-01
        • 1970-01-01
        • 2014-11-12
        • 1970-01-01
        • 1970-01-01
        • 2013-12-09
        相关资源
        最近更新 更多