2013-07-10 175 views
1

我有一个Parent抽象类,并推导出FooBar类。他们都有一个构造函数,参数为string name。它们有一个字符串类型()成员函数,它分别返回“foo”和“bar”。C++:指向“新类()”函数的指针

现在我想用键“foo”和“bar”构建一个dictionnary,其值等于指向创建相应类的新实例的函数的指针。这意味着这些指针会Parent* (*) (const string &)型和执行他们的将是等效于调用和new Bar(name)

我能避免为每个类,将只需要调用新的,并得到一个指向该静态函数创建一个静态函数?

谢谢!

编辑:其实,其背后的目标是在我的应用中实现一个插件系统,插件添加了新的派生类类型。插件作者通常是我的客户,因此,我想让他们拥有最简单优雅的界面。因此,声明和注册工厂功能应该尽可能简单且尽可能短

+0

您可以通过创建一个非静态函数来避免创建一个静态函数。 – juanchopanza

+0

@juabchopanza:呵?这是一个分配器函数,所以它不应该在类实例上工作(与克隆函数相反)...主题 – galinette

+0

正好,我不是说它应该在类实例上工作。 – juanchopanza

回答

1

您需要为您希望能够通过这种方式构建的每个类实例化工厂函数。下面的代码展示了如何做到这一点,而不必再发生静态成员和不能用手写了许多不同的功能:

#include <iostream> 
#include <map> 
#include <functional> 
#include <string> 
#include <memory> 

struct Parent{ 
    Parent(std::string){} 
}; 

struct Child1 : public Parent{ 
    Child1(std::string d) : Parent(d){ 
     std::cout << "Child1 constructor: " << d << std::endl; 
    } 
}; 

struct Child2 : public Parent{ 
    Child2(std::string d) : Parent(d){ 
     std::cout << "Child2 constructor: " << d << std::endl; 
    } 
}; 

template <typename Product, typename Return, typename Parameter> 
Return factory(const Parameter& a){ 
    return Return(new Product(a)); 
} 

std::map<std::string, std::function<Parent*(std::string)> > mrp; 
std::map<std::string, std::function<std::shared_ptr<Parent>(std::string)> > msp; 

int main(){ 
    mrp["Child1"] = &factory<Child1, Parent*, std::string>; 
    mrp["Child2"] = &factory<Child2, Parent*, std::string>; 

    Parent* a = mrp["Child1"]("one"); 
    delete a; 
    std::unique_ptr<Parent> b(mrp["Child2"]("two")); 


    msp["Child1"] = &factory<Child1, std::shared_ptr<Parent>, std::string>; 
    msp["Child2"] = &factory<Child2, std::shared_ptr<Parent>, std::string>; 

    msp["Child1"]("one"); 
    msp["Child2"]("two"); 
} 

试试这个代码here。 此外,你可以看到,这种方法可以“配置”使用std :: shared_ptr的,裸指针,......和不同所有制semanthics,注意行:

std::unique_ptr<Parent> b(mrp["Child2"]("two")); 

然而,任何变化你在问题中简要描述的是,它的一个变种!你所要做的是一个抽象工厂,“标准”实现完全依赖于为你想要构建的每个类创建一个工厂函数。正如我所说的,它不需要是类的静态成员,它可以是非成员的非友元函数,但这并没有太大的改变(除了更好的封装,以及构造函数的存在公众或工厂成为朋友,这在某种程度上打破封装)。

对象工厂在loki中以非常优雅的方式实现。请参阅Modern C++(Alexandrescu),详细讨论设计模式本身以及Loki给出的实现。

至于你的编辑:在洛基注册是甜而简单的(引自书):

// Implementation module for class Line 
// Create an anonymous namespace 
// to make the function invisible from other modules 
namespace 
{ 
Shape* CreateLine() 
{ 
return new Line; 
} 
// The ID of class Line 
const int LINE = 1; 
// Assume TheShapeFactory is a singleton factory 
// (see Chapter 6) 
const bool registered = 
TheShapeFactory::Instance().RegisterShape( 
LINE, CreateLine); 
} 
+0

@DyP实际上,您可以通过为每个类编写一个*非静态*函数来轻松满足要求:-) – juanchopanza

+0

@juanchopanza但是这需要您可以从类的字典中创建虚拟实例。考虑到类不变量,const成员,ref成员,RAII等等,这可能很困难。对吧? – dyp

+0

@DyP为什么你不能创建一个对象并返回它?无论如何,我并不是在暗示这样做,只是指出需求是混淆的。 – juanchopanza

4

你描述的是一个工厂方法模式。 这里有一个链接:http://en.wikipedia.org/wiki/Factory_method_pattern 静态函数或某种工厂基本上是你想要的东西。

为什么要避免静态创建方法?

+0

嗨, 感谢您的链接。实际的代码更加复杂,并且是一个插件系统,插件将新的派生类类型添加到应用程序中。我希望插件作者尽可能少编写代码(他们是客户......),尽可能多地包含在头文件中。 – galinette

8

我可以避免为每个只需调用new的类创建静态函数,并获取指向此静态函数的指针吗?

是的,你可以创建一个通用的功能:

template <typename T> 
ptr_type<Parent> create() { 
    return new T(); 
} 

...然后取它的地址(但你需要获得一个地址每派生类型)。

请注意,我有明确使用T*作为返回类型 - 原始指针不应该自己的记忆......一个合适的智能指针应改为在这里使用。

+0

您确实应该使用ptr_type ,否则您将无法将它们放入字典中。 – riv

+0

我仍然无法看到如何在字典中添加指向模板函数的指针。 –

+0

@WouterHuysentruit'&创建'和'&创建'。就像一个指向任何其他函数的指针。(当然,它们需要相同的返回类型) –