我有一个Parent
抽象类,派生了Foo
和Bar
类。它们都有一个带string name
参数的构造函数。它们有一个字符串type()成员函数,它分别返回“foo”和“bar”。
现在我想建立一个带有键“foo”和“bar”的字典,值等于指向创建相应类的新实例的函数的指针。这意味着这些指针的类型为Parent* (*) (const string &)
,执行它们将等同于调用new Foo(name)
和new Bar(name)
我可以避免为每个只调用new的类创建一个静态函数,并获得指向这个静态函数的指针吗?
谢谢!
编辑:实际上,这背后的目标是在我的应用程序中实现一个插件系统,插件添加新的Derived类类型。插件作者通常是我的客户,因此,我想让他们拥有最简单和优雅的界面。因此,声明和注册工厂功能应该尽可能简单和简短
答案 0 :(得分:8)
我可以避免为每个只调用new的类创建一个静态函数,并获得指向这个静态函数的指针吗?
是的,您可以创建一个通用函数:
template <typename T>
ptr_type<Parent> create() {
return new T();
}
...然后获取其地址(但您需要获取一个地址每个派生类型)。
请注意,我明确地不使用T*
作为返回类型 - 原始指针永远不应该拥有内存......应该在这里使用适当的智能指针。
答案 1 :(得分:4)
您描述的是工厂方法模式。 这是一个链接:http://en.wikipedia.org/wiki/Factory_method_pattern 静态功能或某种工厂基本上就是你想要的东西。
为什么要避免使用静态创建方法?
答案 2 :(得分: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给出的实现。
至于你的编辑: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);
}