c++静态工厂构造函数

C++ static factory constructor

本文关键字:构造函数 工厂 静态 c++      更新时间:2023-10-16

我正在制作模拟过程中,它需要创建多个相当相似的模型。我的想法是有一个名为Model的类,并使用静态工厂方法来构造模型。例如;Model::createTriangleModel::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;
}

关于这个的一些问题:

  • 这是一种被接受的、干净的制作对象的方式吗?
  • 返回的引用是否总是确保正确地删除对象?
  • 有没有办法做到这一点没有指针?

只是为了记录,下面是这个程序在适当的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
}

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

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

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

这似乎需要对象的额外副本,但实际上,返回值优化通常会导致该副本被优化掉。

这是一种被接受的、干净的制造物体的方式吗?

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

用构造函数代替工厂函数。

这就是它们的作用。

返回的引用是否总是确保正确地删除对象?

引用是不相关的,除非它误导了函数的用户。

在你的例子中,引用显然误导了你自己没有销毁动态分配的对象,而只是复制它。

最好返回一个智能指针。

但是正如已经提到的,最好放弃工厂函数的想法。

它们在这里完全没有必要。

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

不,如果"this"指的是动态分配,但你可以而且应该使用构造函数而不是工厂函数。


的例子:

#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;
}

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

有两种可能的解决方法:

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

    在堆栈上分配它就足够了
    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);
    }
    

这绝对是一种糟糕的创建对象的方式。每次调用createWithID的时候,一个新的Object就会在自由存储区被构造,并且永远不能被销毁。

你应该重写createWithID为:

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

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

如果你想启用多态对象,你应该使用类似wheels::value_ptr的东西

除非您使用多态性,否则您的工厂函数没有理由返回任何类型的指针,它们可以按值返回对象。任何现代编译器都会做返回值优化,所以没有复制。

如果你是在一个"被接受和干净"的方式之后,那么这听起来相当基于意见,取决于这个类将如何使用,但我要做的是保持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类。如果需要,您的create函数甚至可以分布在多个文件中,或者成为Builder或Factory类的一部分。用法如下:

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