我最近在static member functions
中找到了一个使用pure abstract classes
初始化指向其派生类对象的指针的示例。
我想知道,如果它是一种设计模式,或者它是一个很好的编程实践?下面的代码只是一个ilustration(例如defineRectangle() and defineCircle()
成员
函数):
class Shape;
typedef std::unique_ptr<Shape> shape_ptr;
class Shape{
public:
Shape(){};
virtual ~Shape(){};
virtual void draw() const = 0;
virtual float area() const = 0;
virtual shape_ptr clone() const = 0;
virtual shape_ptr create() const = 0;
static shape_ptr defineRectangle(int, int );
static shape_ptr defineCircle(float);
};
shape_ptr Shape::defineRectangle(int height, int width){
shape_ptr ptrRectangle = shape_ptr(new Rectangle(height, width));
return (ptrRectangle);
}
shape_ptr Shape::defineCircle(float radius){
shape_ptr ptrCircle = shape_ptr(new Circle(radius));
return (ptrCircle);
}
最终目标是定义derived classes
的容器。例如:
std::vector<std::unique_ptr<Shape> > vect;
然后我们可以通过调用Shape
类的静态成员函数在容器中添加派生类:
vect.push_back(Shape::defineCircle(10));
vect.push_back(Shape::defineRectangle(5, 4));
或直接没有任何界面:
vect.push_back(std::unique_ptr<Shape>(new Circle(10)));
vect.push_back(std::unique_ptr<Shape>(new Rectangle(5,4)));
应该首选两种在容器中添加派生类的方法中的哪一种?为什么? 完整代码可在以下link中找到 它上面的任何灯都非常受欢迎; - )
答案 0 :(得分:1)
由于存在std::unique_ptr
,我假设编译器支持C ++ 11。在这种情况下,让我提供第三种选择:
vect.emplace_back(new Circle(10));
vect.emplace_back(new Rectangle(5,4));
(关于.emplace_back
:push_back vs emplace_back)
使用此功能,您无需重复shape_ptr
,并且无论何时添加新子类,都无需向Shape
声明新的工厂方法。
编辑:在C ++ 14中,您可以使用std::make_unique
删除原始new
来电。
vect.emplace_back(std::make_unique<Circle>(10));
vect.emplace_back(std::make_unique<Rectangle>(5, 4));
答案 1 :(得分:1)
我想知道,如果它是一种设计模式,或者它是一个很好的编程习惯吗?
是的,它是factory pattern的变体。
基本上,它允许你有一个方法,根据该方法的参数,将分派动态创建正确的派生对象类型。这允许您在代码中使用相同的“工厂”功能,如果工厂方法创建的基础对象有任何更改或添加,您不必更改实际调用“工厂”功能的代码。因此,它是一种封装形式,它将对象创建的任何更改隔离到“工厂”后面的代码段,而不是调用“工厂”的代码。例如,使用工厂,添加工厂方法可以创建的新类型相对微不足道,但是之前调用工厂的代码都不会发生变化。您只需要为要创建的新对象创建一个新的派生类,并且对于任何需要该新对象的新代码,您都需要传递正确的新参数。所有旧的参数仍然有效,并且在返回的指针类型等方面,代码中不需要进行任何更改。
在工厂中使用智能指针的原因是为了避免在指针所有权不明确时可能发生的内存泄漏。例如,工厂必须返回指针,因为它是动态地创建对象。然后问题是谁清理指针以避免悬空指针或内存泄漏?智能指针清除了这个所有权问题,并保证当其他对象仍然指向该内存时,内存不会被无意中清除,或者内存不会因为最后一个指向该内存位置的指针超出范围而丢失delete
号召了它。
答案 2 :(得分:1)
我建议不要将工厂方法放在基类中,因为从技术上讲,Shape
对Rectangle
或Circle
一无所知。如果您添加新形状,例如Donut
,那么您将做什么?向Shape
添加新工厂方法?你很快就会把界面弄得乱七八糟。所以,IMO,第二种方法会更好。
如果你想减少每次都必须创建shape_ptr
a的详细程度,你总是可以将工厂方法移动到适当的子类:
class Circle : public Shape
{
// ...
public:
static shape_ptr make(float radius)
{
return shape_ptr(new Circle(radius));
}
};
// ...
vect.push_back(Circle::make(5.0f));