C++ static factory constructor
我正在进行模拟,它需要创建多个非常相似的模型。我的想法是创建一个名为model的类,并使用静态工厂方法来构造模型。例如:Model::CreateTriangle或Model::CreateFromFile。我从以前的Java代码中获取了这个想法,并在C++中寻找实现这一点的方法。
以下是我到目前为止的想法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #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++中的样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 } |
这可能不是人们通常所说的"工厂",因为工厂通常涉及一些动态类型选择。不过,术语"命名构造函数"有时用于引用返回类实例的静态成员函数。
您的代码当前包含内存泄漏:任何使用
1 2 3 4 5 6 | static Object createWithID(int id) { Object obj; obj.id = id; return obj; } |
这似乎需要对象的额外副本,但实际上,返回值优化通常会导致该副本被优化掉。
Is this an accepted and clean way of making objects?
很遗憾,这是可以接受的,但不干净。
不使用工厂函数,只使用构造函数。
这就是他们的目的。
Does the returned reference always ensure correct removal of the object?
引用是无关的,除非它误导了函数的用户。
在您的示例中,引用显然误导了您自己,使您不能破坏动态分配的对象,而只是复制它。
最好返回一个智能指针。
但是正如前面提到的,放弃工厂功能的想法更好。
他们在这里完全没有必要。
Is there any way to do this without pointers?
不,如果"this"指的是动态分配,则不是,但是您可以并且应该使用构造函数而不是工厂函数。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #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; } |
除非您使用多态性,否则工厂函数没有理由返回任何类型的指针,它们只能按值返回对象。任何现代编译器都会执行返回值优化,因此没有副本。
如果你追求的是一种"被接受和干净"的方式,那么这听起来是基于意见的,并且取决于这个类将如何使用,但是我要做的是尽可能少地使用
1 2 3 4 5 6 7 8 9 10 | namespace Simulation { class Model { private: int id_; public: explicit Model(int id) : id_(id) {} // minimum required to do the job... }; } |
然后,我将定义分别创建不同风格的
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } } |
这样,如果你发现你需要另一种口味的
1 2 3 4 5 | int main() { Simulation::Model m1(0); Simulation::Model m2 = Simulation::createTriangle(1); Simulation::Model m3 = Simulation::createSquare(2); } |
通过调用
有两种可能的解决方法:
1 2 | Object tmp; tmp.id = id; |
使用C++ 11智能指针,让他们处理内存。
1 2 3 4 5 6 7 | #include <memory> static std::unique_ptr<Object> createWithID(int id) { std::unique_ptr<Object> tmp(new Object()); tmp->id = id; return std::move(tmp); } |
这是创建对象的绝对糟糕的方法。每次调用
您应将
1 2 3 4 5 | static Object createWithID(int id) { Object obj; obj.id = id; return obj; } |
或者更好,您可以为您的
如果要启用多态对象,应使用wheels::value_ptr之类的工具。