C ++静态工厂构造函数

时间:2014-05-24 12:22:52

标签: c++ constructor factory

我正在进行模拟,它需要创建多个相似的模型。我的想法是有一个名为Model的类,并使用静态工厂方法来构建模型。例如; Model :: createTriangle Model :: createFromFile 。我从之前的Java代码中接受了这个想法,并且正在寻找在C ++中实现它的方法。

这是我到目前为止所提出的:

#include <iostream>

class Object {
    int id;

public:
    void print() { std::cout << id << std::endl; }

    static Object &createWithID(int id) {
        Object *obj = new Object();

        obj->id = id;

        return *obj; 
    }
};

int main() {
    Object obj = Object::createWithID(3);

    obj.print();

    return 0;
}

有关此问题的一些问题:

  • 这是一种接受且干净的制作物品的方法吗?
  • 返回的引用是否始终确保正确删除对象?
  • 如果没有指针,有没有办法做到这一点?

6 个答案:

答案 0 :(得分:6)

只是为了记录,以下是这个程序在适当的C ++中的样子:

class Object
{
    int id;

    // private constructor, not for general use
    explicit Object(int i) : id(i) { }

public:
    static Object createWithID(int id)
    {
        return Object(id);
    }
};

int main()
{
    Object obj1 = Object::createWithID(1);
    auto obj2 = Object::createWithID(2);   // DRY

    // return 0 is implied
}

这可能不是人们通常所说的工厂&#34;,因为工厂通常涉及一些动态类型选择。术语&#34;命名构造函数&#34;但有时会使用它来引用返回类实例的静态成员函数。

答案 1 :(得分:5)

您的代码当前包含内存泄漏:必须使用new清除使用delete创建的任何对象。 createWithID方法最好不要使用new,看起来像这样:

static Object createWithID(int id) 
{
    Object obj;
    obj.id = id;
    return obj; 
}

这似乎需要额外的对象副本,但实际上return value optimization通常会导致此副本被优化掉。

答案 2 :(得分:2)

  

这是一种被接受且干净的制作物品的方法吗?

(不幸的是)它被接受但它不干净。

只使用构造函数而不是工厂函数。

这就是他们的目标。

  

返回的引用是否始终确保正确删除对象?

该引用无关紧要,除非误导该函数的用户。

在你的例子中,引用显然误导了你不破坏动态分配的对象,而只是复制它。

最好返回智能指针。

但正如已经提到的,放弃工厂职能的想法更好。

他们在这里完全没必要。

  

如果没有指针,有没有办法做到这一点?

不,不是,如果&#34;这&#34;指动态分配,但您可以而且应该使用构造函数而不是工厂函数。


示例:

#include <iostream>

namespace better {
    using std::ostream;

    class Object
    {
    public:
        auto id() const -> int { return id_; }
        explicit Object( int const id): id_( id ) {}
    private:
        int id_;
    };

    auto operator<<( ostream& stream, Object const& o )
        -> ostream&
    { return (stream << o.id()); }
}  // namespace better

auto main()
    -> int
{
    using namespace std;
    cout << better::Object( 3 ) << endl;
}

答案 3 :(得分:1)

这是创建对象的绝对可怕方法。每次调用createWithID时,都会在免费商店上构建一个永远无法销毁的新Object

您应该将createWithID重写为:

static Object createWithID(int id) {
    Object obj;
    obj.id = id;
    return obj; 
}

或者更好的是,您可以为Object个对象提供构造函数。

如果要启用多态对象,则应使用类似wheels::value_ptr的内容。

答案 4 :(得分:1)

通过调用Object *obj = new Object();,您可以在堆上分配内存。在该语句后面的行中,您确实返回对该对象的引用。到目前为止,这么好,但你永远不会删除你创建的对象以实际释放内存。通过多次调用该函数,您将在内存泄漏中运行。

有两种可能的解决方法:

  1. static Object createWithID(int id);将返回您创建的Object的副本,因此使用

    将其分配到堆栈就足够了
    Object tmp;
    tmp.id = id;
    
  2. 使用c ++ 11智能指针让它们处理内存。

    #include <memory>
    static std::unique_ptr<Object> createWithID(int id)
    {
        std::unique_ptr<Object> tmp(new Object());
        tmp->id = id;
        return std::move(tmp);
    }
    

答案 5 :(得分:1)

除非您使用多态,否则您的工厂函数没有理由返回任何类型的指针,它们只能按值返回对象。任何现代编译器都会执行return value optimization,因此没有副本。

如果您正在接受&#34;接受并清洁&#34;然后,这听起来很基于意见,并取决于如何使用这个类,但我要做的是保持Model的定义尽可能小。只包含正常使用所需的最少构造函数所需的工作:

namespace Simulation {
  class Model {
   private: 
    int id_;
   public:
    explicit Model(int id) : id_(id) {}

    // minimum required to do the job...
  };
}

然后,我将定义函数以分别创建各种风格的Model。例如,作为名称空间中的非成员,非朋友函数:

namespace Simulation {  
  Model createTriangle(int id) {
    Model model(id);
    // do whatever you need to do to make it a triangle...
    return model;
  }

  Model createSquare(int id) {
    Model model(id);
    // do whatever you need to do to make it a square...
    return model;
  }  
}

这样,如果您发现需要另一种Model,则不需要更改Model课程。如果需要,您的创建函数甚至可以分布在多个文件中,或者成为BuilderFactory类的一部分。用法如下:

int main() {
  Simulation::Model m1(0);
  Simulation::Model m2 = Simulation::createTriangle(1);
  Simulation::Model m3 = Simulation::createSquare(2);
}