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

C++ 作为右值引用传递的对象在尝试应用装饰器模式时导致错误,c++,inheritance,decorator,C++,Inheritance,Decorator,我试图学习装饰器模式,并将以下代码组合在一起。当我显式地创建对象,然后应用装饰器时,它会起作用(例如,代码中的circleOne和circleTwo) 但是,当我隐式创建对象时(circleThree和circleFour) 然后,最后一个对象circleFour覆盖第三个对象circleThree的数据 完整代码如下: #include <iostream> using namespace std; struct Shape { virtual string str()

我试图学习装饰器模式,并将以下代码组合在一起。当我显式地创建对象,然后应用装饰器时,它会起作用(例如,代码中的
circleOne
circleTwo

但是,当我隐式创建对象时(
circleThree
circleFour

然后,最后一个对象
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

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

首先,您的代码具有未定义的行为,因为您正在将临时对象传递给类的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"
};

所以,你基本上有两个选择

  • 不要装饰临时物品。最明显也是最无聊的

  • 使您的装饰者成为模板,并使他们拥有自己的装饰形状(如果需要):

    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>;
    
    greenCircle
    的类型推断为
    Colored
    ,生成的圆是它的私有成员,如果您传递给它一个已经存在的对象

    Circle c{2.0};
    Colored redCircle(c, "red");
    
    redCircle
    的类型是有色的,因此它的成员只是对
    c
    的引用

    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");
    
    Circle c{2.0};
    Colored redCircle(c, "red");