0

I've got the following code currently (not working):

#include <iostream>
#include <vector>

class Circle;
class Rectangle;

class Shape {
private:
    Shape() {};
public:
    virtual ~Shape() {};
    friend class Circle;
    friend class Rectangle;
};

class Creator {
public:
    virtual ~Creator() {};
    virtual Shape* create() = 0;
    virtual bool equals(Shape& s) { return false; };
};

class Circle : public Shape {
private:
    Circle() : Shape() {};
public:
    class CircleCreator : public Creator {
    public:
        virtual Shape* create() { return new Circle(); };
        virtual bool equals(Shape& other_shape) { return false; };
    };
};

class Rectangle : public Shape {
private:
    Rectangle() : Shape() {};
public:
    class RectangleCreator : public Creator {
    public:
        virtual Shape* create() { return new Rectangle(); };
        virtual bool equals(Shape& other_shape) { return false; };
    };
};

int main() {
    /* First step, build the list */
    std::vector<Shape*> shapeList;
    std::vector<Shape*>::iterator it;
    Rectangle::RectangleCreator rc;
    Circle::CircleCreator cc;
    Shape* s = cc.create();
    Shape* s1 = rc.create();
    shapeList.push_back(s);
    shapeList.push_back(s1);

    /* Second step: check if we've got a shape starting from a creator */
    for (it = shapeList.begin(); it != shapeList.end(); ++it) {
        if (rc.equals(**it)) {
            std::cout << "same shape" << std::endl;
        }
    }
    return 0;
}

My goal is to use a factory pattern and avoid the creation of a new object if in a list I've got already that object. I tried to use a double dispatch pattern but it isn't easy to apply in this case. How can I do?

Edit: Since the code is used in a "critical" path, I want to avoid RTTI like dynamic_cast and so on.

4

2 回答 2

1

也许这样的事情可以使用成员变量来完成

#include <iostream>
#include <vector>

enum
{
CIRCLE,
RECTANGLE
};

class Circle;
class Rectangle;

class Shape {
private:
    Shape() {};
public:
    unsigned shapeType;
    virtual ~Shape() {};
    friend class Circle;
    friend class Rectangle;
};

class Creator {
public:
unsigned shapeType;
    virtual ~Creator() {};
    virtual Shape* create() = 0;
    bool equals(Shape& s) { return (this->shapeType == s.shapeType); };
};

class Circle : public Shape {
private:
    Circle() : Shape() {shapeType=CIRCLE;};
public:
    class CircleCreator : public Creator {
    public:
        CircleCreator() {shapeType=CIRCLE;};
        virtual Shape* create() { return new Circle(); };
    };
};

class Rectangle : public Shape {
private:
    Rectangle() : Shape() {shapeType=RECTANGLE;};
public:
    class RectangleCreator : public Creator {
    public:
        RectangleCreator() {shapeType=RECTANGLE;};
        virtual Shape* create() { return new Rectangle(); };
    };
};

int main() {
    /* First step, build the list */
    std::vector<Shape*> shapeList;
    std::vector<Shape*>::iterator it;
    Rectangle::RectangleCreator rc;
    Circle::CircleCreator cc;
    Shape* s = cc.create();
    Shape* s1 = rc.create();
    shapeList.push_back(s);
    shapeList.push_back(s1);

    /* Second step: check if we've got a shape starting from a creator */
    for (it = shapeList.begin(); it != shapeList.end(); ++it) {
        if (rc.equals(**it)) {
            std::cout << "same shape" << std::endl;
        }
    }
    return 0;
}

或者这个 - 使用虚函数返回类型

#include <iostream>
#include <vector>

enum
{
    CIRCLE,
RECTANGLE,
UNKNOWN
};
class Circle;
class Rectangle;

class Shape {
private:
    Shape() {};
public:
    virtual ~Shape() {};
    friend class Circle;
    friend class Rectangle;
    virtual unsigned iAmA(){return UNKNOWN;};
};

class Creator {
public:
    virtual ~Creator() {};
    virtual Shape* create() = 0;
    virtual bool equals(Shape& s) { return false; };
};

class Circle : public Shape {
private:
    Circle() : Shape() {};
    virtual unsigned iAmA(){return CIRCLE;};
public:
    class CircleCreator : public Creator {
    public:
        CircleCreator() {};
        virtual Shape* create() { return new Circle(); };
        virtual bool equals(Shape& other_shape) { return (CIRCLE == other_shape.iAmA()); };
    };
};

class Rectangle : public Shape {
private:
    Rectangle() : Shape() {};
    virtual unsigned iAmA(){return RECTANGLE;};
public:
    class RectangleCreator : public Creator {
    public:
        RectangleCreator() {};
        virtual Shape* create() { return new Rectangle(); };
        virtual bool equals(Shape& other_shape) { return (RECTANGLE == other_shape.iAmA()); };
    };
};

int main() {
    /* First step, build the list */
    std::vector<Shape*> shapeList;
    std::vector<Shape*>::iterator it;
    Rectangle::RectangleCreator rc;
    Circle::CircleCreator cc;
    Shape* s = cc.create();
    Shape* s1 = rc.create();
    shapeList.push_back(s);
    shapeList.push_back(s1);

    /* Second step: check if we've got a shape starting from a creator */
    for (it = shapeList.begin(); it != shapeList.end(); ++it) {
        if (rc.equals(**it)) {
            std::cout << "same shape" << std::endl;
        }
    }
    return 0;
}
于 2015-03-07T08:17:27.050 回答
0

我不确定您要做什么,但我想这可以为您指明方向

enum class Shapes
{
    Rectangle,
    Circle,
    ...
};

class Shape
{
private:
    Shapes m_shape;

protected:
    Shape(Shapes shape)
    {
        m_shape = shape;
    }

public:
    Shapes GetShape() { return m_shape; } // this is used to check whether two shapes are equal

    virtual ~Shape() = default;
};

现在对于工厂模式,你会这样做:

class ShapeFactory
{
public:
    static Shape* CreateShape(Shapes shape)
    {
        switch (shape)
        {
            case Shapes::Circle:
                return new Circle();
            // etc.
        }
    }
};

这对我来说感觉非常多余而且不是很聪明。此外,这可以将大量代码放在一个地方。

对于调度,你可以这样做(我假设,我不是这个概念的粉丝,因为它可以通过简单的模板使用变得不那么冗长)

class ShapeCreator
{
public:
    virtual Shape* Create() = 0;
    virtual ~ShapeCreator() = default;
};

class Circle : public Shape
{
public:
    class Creator : ShapeCreator
    {
    public:
        Shape* Create() { return new Circle(); }
    };

    Circle() : Shape(Shapes::Circle)
    {}
};

bool SomethingWithCircle()
{
    Circle::Creator circleCreator;
    Shape* first = circleCreator.Create();
    Shape* second = circleCreator.Create();

    // notice memleak here
    return first->GetShape() == second->GetShape();
}

如果使用 C++11,您可以走得更远,避免使用适当的模板自慰技术来避免整个想法/无论如何我觉得这很像 java。(仍然可以应用于 C++11 之前的版本,只是无法指定参数。)

template<class T>
class ShapeCreator
{
public:
    template<class... TParams>
    static T* Create(TParams&&... parameters) { return new T(std::forward<TParams>(parameters)...); }
};

class Rectangle : public Shape
{
private:
    int m_width;
    int m_height;

public:
    Rectangle(int width, int height) : Shape(Shapes::Rectangle)
    {
        m_width = width;
        m_height = height;
    }
};

bool DoSomethingWithRectangles()
{
    Rectangle* first  = ShapeCreator<Rectangle>::Create(10, 15);
    Shape* second     = ShapeCreator<Rectangle>::Create(20, 25);

    // notice memleak here
    return first->GetShape() == second->GetShape();
}

TL;DR
您实际上并不需要 RTTI,但您需要将类型信息存储在基本类型中的某个位置。我正在使用enum Shapes这个。
Factory 和 Dispatch 似乎都是一个好主意,但是在使用它们时您仍然需要在某个地方进行动态转换。
您可以使用模板替换这两种模式,但是一旦您获得基础对象的向量,您仍然需要dynamic_cast在某些时候这样做。
我没有对此进行任何测量,但我对使用虚拟函数和动态转换的性能比较非常感兴趣,因为我想它们会非常相似......

尾注:
请注意,我个人觉得在定义基本接口的类上使用类似equalsoroperator==的方法不是很明智,因为有两种可能的结果:

  1. equals是虚拟的-> 慢但可以接受
  2. equalsis not virtual -> 不能在继承类型中使用以实际进行更高级/相关的比较,打破了Open to extension, closed for modification

显然,如果您不定义equals,则每次都必须编写比较代码。或者可能Comparison通过特征使用一些具有可能特化的模板类,再次产生最佳性能而没有代码重复。

一般来说,您可以问自己“为什么没有像 java 中的基对象和反射或 c++ 中的 c# 那样?它可以让我使用所有这些漂亮而聪明的模式。” 答案是模板。为什么它是运行时的,什么时候你可以在编译时做呢?

于 2015-03-07T08:29:51.453 回答