尝试应用装饰器模式时,作为右值引用传递的对象会导致错误

时间:2018-12-24 15:46:16

标签: c++ inheritance decorator

我正在尝试学习装饰器模式,并将以下代码组合在一起。当我显式创建对象然后应用装饰器时(例如代码中的circleOnecircleTwo),它可以工作。

    Circle c1{5.6};
    ColoredShape cc1{c1, "green"};
    TransparentShape circleOne{cc1, 67};

    Circle c2{9.5};
    TransparentShape tc2{c2, 25};
    ColoredShape circleTwo{tc2, "yellow"};

但是,当我隐式创建对象(circleThreecircleFour

    TransparentShape circleThree{
            ColoredShape{
                    Circle{50.6},
                    "green"
            },
            67
    };

    ColoredShape circleFour{
            TransparentShape{
                    Circle{90.5},
                    25
            },
            "yellow"
    };

然后,最终对象circleFour将覆盖第三个对象circleThree的数据。

完整代码如下:

#include <iostream>

using namespace std;

struct Shape {
    virtual string str() const = 0;
};

struct Circle : Shape {
    double radius;

    explicit Circle(const double radius) :
            radius{radius} {}

    void resize(const double factor) { radius *= factor; }

    string str() const override {
        return "A circle of radius " + to_string(radius);
    }
};

struct Square : Shape {
    double length;

    explicit Square(const double length) :
            length{length} {}

    void resize(const double factor) {
        length *= factor;
    }

    string str() const override {
        return "A square of side length " + to_string(length);
    }
};

struct ColoredShape : Shape {
    const Shape &shape;
    string color;

    ColoredShape(const Shape &shape, const string &color) :
            shape{shape}, color{color} {}

    //ColoredShape(Shape &&shape, const string &color) :
    //        shape{shape}, color{color} {}

    string str() const override {
        return shape.str() + " has the color " + color;
    }
};

struct TransparentShape : Shape {
    const Shape &shape;
    int transparency;

    TransparentShape(const Shape &shape, const int transparency) :
            shape(shape), transparency(transparency) {}

    //TransparentShape(Shape &&shape, const int transparency) :
    //        shape(shape), transparency(transparency) {}

    string str() const override {
        return shape.str() + " has " + to_string(transparency) +
               "% transparency";
    }
};

int main() {


    Circle c1{5.6};
    ColoredShape cc1{c1, "green"};
    TransparentShape circleOne{cc1, 67};

    Circle c2{9.5};
    TransparentShape tc2{c2, 25};
    ColoredShape circleTwo{tc2, "yellow"};

    TransparentShape circleThree{
            ColoredShape{
                    Circle{50.6},
                    "green"
            },
            67
    };

    ColoredShape circleFour{
            TransparentShape{
                    Circle{90.5},
                    25
            },
            "yellow"
    };

    cout << circleOne.str() << endl;
    cout << circleTwo.str() << endl;
    cout << circleThree.str() << endl;
    cout << circleFour.str() << endl;
    return 0;
} 

输出如下:

A circle of radius 5.600000 has the color green has 67% transparency
A circle of radius 9.500000 has 25% transparency has the color yellow
A circle of radius 90.500000 has 25% transparency has the color green has 67% transparency
A circle of radius 90.500000 has 25% transparency has the color yellow

如您所见,第三个圆的数据被第四个圆替换,并且透明装饰器被应用了两次。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:0)

首先,您的代码具有未定义的行为,因为您正在将临时对象传递给类的const ref成员。但是,当临时范围超出范围时,它会被销毁,并且您会有悬挂的引用。

这是错误的代码,

struct ColoredShape : Shape {
    const Shape &shape;
    string color;

    ColoredShape(const Shape &shape, const string &color) :    // const ref here !!!
            shape{shape}, color{color} {}

    //ColoredShape(Shape &&shape, const string &color) :
    //        shape{shape}, color{color} {}

    string str() const override {
        return shape.str() + " has the color " + color;
    }
};




ColoredShape circleFour{
    TransparentShape{    // this is a temporary
            Circle{90.5},
            25
    },
    "yellow"
};

答案 1 :(得分:0)

因此,您基本上有两个选择。

  1. 请勿装饰临时对象。最明显,最无聊。

  2. 使装饰器模板化,并根据需要使它们自己具有装饰形状:

    template<class Decorated> class Colored: Shape {
        std::string color;
        Shape;
    public:
        Colored(Shape &&shape, std::string color): shape(std::forward<Shape>(shape)), color(std::move(color)) {}
    };
    template<class Decorated> Colored(Decorated &&, std::string) -> Colored<Decorated>;
    

这样,如果您进行构建

Colored greenCircle(Circle{1.0}, "green");

greenCircle的类型推导为Colored<Circle>,结果圆是其私有成员,并且如果您将其传递给已存在的对象,则为该对象

Circle c{2.0};
Colored redCircle(c, "red");

redCircle的类型为Colored<Circle &>,因此其成员只是对c的引用。