我刚刚了解了装饰器模式,并尝试编写一个使用该代码的示例。这个例子是关于饮料和一些调味品的。在装饰器内部,我有一个 引用变量 到饮料。提供的饮料是Decaf
和 Espresso
.可用的调味品是Soy
和 Caramel
.如果我定义一个 Decaf
不止一个 Caramel
例如,我得到的结果只是一个带有一个装饰器的 Decaf。所以定义 Caramel
-> Caramel
-> Decaf
给我 Caramel
-> Decaf
.定义 Caramel
-> Soy
-> Caramel
-> Decaf
工作正常。定义 Caramel
-> Soy
-> Caramel
-> Caramel
-> Decaf
给我 Caramel
-> Soy
-> Caramel
-> Decaf
.长话短说,我不能一个接一个地吃两种或多种相同类型的调味品。它们只成为一种调味品。如果我使用指针它工作正常。
编码:
#include <iostream>
//#include "Decaf.h"
//#include "Espresso.h"
//#include "SoyDecorator.h"
//#include "CaramelDecorator.h"
class Beverage
{
public:
virtual std::string GetDescription() const = 0;
virtual int GetCost() const = 0;
};
class CondimentDecorator : public Beverage
{
public:
Beverage& beverage;
CondimentDecorator(Beverage& beverage) : beverage(beverage) {}
};
class Espresso : public Beverage
{
virtual std::string GetDescription() const override
{
return "Espresso";
}
virtual int GetCost() const override
{
return 5;
}
};
class Decaf : public Beverage
{
virtual std::string GetDescription() const override
{
return "Decaf";
}
virtual int GetCost() const override
{
return 4;
}
};
class CaramelDecorator : public CondimentDecorator
{
public:
CaramelDecorator(Beverage& beverage) : CondimentDecorator(beverage) {}
virtual std::string GetDescription() const override
{
return this->beverage.GetDescription() + " with Caramel";
}
virtual int GetCost() const override
{
return this->beverage.GetCost() + 2;
}
};
class SoyDecorator : public CondimentDecorator
{
public:
SoyDecorator(Beverage& beverage) : CondimentDecorator(beverage) {}
virtual std::string GetDescription() const override
{
return this->beverage.GetDescription() + " with Soy";
}
virtual int GetCost() const override
{
return this->beverage.GetCost() + 1;
}
};
int main()
{
Decaf d;
SoyDecorator s(d);
CaramelDecorator c(s);
CaramelDecorator cc(c);
std::cout << cc.GetDescription() << std::endl;
std::cout << cc.GetCost() << std::endl;
}
输出:
Decaf with Soy with Caramel
7
// Expected:
// Decaf with Soy with Caramel with Caramel
// 9
这是相同的代码,但使用指针并且工作正常:
https://ideone.com/7fpGSp
最佳答案
随着从指针到引用的切换,OP 构造函数签名变得与(默认)复制构造函数非常相似。
CondimentDecorator(Beverage &beverage) : beverage(beverage) {}
对比
CondimentDecorator(const Beverage&); // generated by compiler
首先,我认为删除复制构造函数就足够了,但编译器仍然尝试使用已删除的构造函数,因为它不能再使用相应的投诉。
最后,我能够通过提供 resp 来解决 OP 的问题。阻止使用复制构造函数的候选对象。
(实际上不再需要删除复制构造函数,但我把它留在了。)
class CondimentDecorator : public Beverage
{
public:
Beverage& beverage;
CondimentDecorator(Beverage &beverage) : beverage(beverage) {}
CondimentDecorator(CondimentDecorator &beverage) : beverage(beverage) {}
CondimentDecorator(const CondimentDecorator&) = delete;
};
对于派生类也必须这样做:
class CaramelDecorator : public CondimentDecorator
{
public:
CaramelDecorator(Beverage &beverage) : CondimentDecorator(beverage) {}
CaramelDecorator(CaramelDecorator &beverage) : CondimentDecorator(beverage) {}
//CaramelDecorator(const CaramelDecorator&) = delete;
virtual std::string GetDescription() const override
{
return this->beverage.GetDescription() + " with Caramel";
}
virtual int GetCost() const override
{
return this->beverage.GetCost() + 2;
}
};
我只修复了
CaramelDecorator
用于演示,但实际上,这必须对 class CondimentDecorator
的所有派生类进行。 .OP的固定MCVE:
#include <iostream>
//#include "Decaf.h"
//#include "Espresso.h"
//#include "SoyDecorator.h"
//#include "CaramelDecorator.h"
class Beverage
{
public:
virtual std::string GetDescription() const = 0;
virtual int GetCost() const = 0;
};
class CondimentDecorator : public Beverage
{
public:
Beverage& beverage;
CondimentDecorator(Beverage &beverage) : beverage(beverage) {}
CondimentDecorator(CondimentDecorator &beverage) : beverage(beverage) {}
CondimentDecorator(const CondimentDecorator&) = delete;
};
class Espresso : public Beverage
{
virtual std::string GetDescription() const override
{
return "Espresso";
}
virtual int GetCost() const override
{
return 5;
}
};
class Decaf : public Beverage
{
virtual std::string GetDescription() const override
{
return "Decaf";
}
virtual int GetCost() const override
{
return 4;
}
};
class CaramelDecorator : public CondimentDecorator
{
public:
CaramelDecorator(Beverage &beverage) : CondimentDecorator(beverage) {}
CaramelDecorator(CaramelDecorator &beverage) : CondimentDecorator(beverage) {}
//CaramelDecorator(const CaramelDecorator&) = delete;
virtual std::string GetDescription() const override
{
return this->beverage.GetDescription() + " with Caramel";
}
virtual int GetCost() const override
{
return this->beverage.GetCost() + 2;
}
};
class SoyDecorator : public CondimentDecorator
{
public:
SoyDecorator(Beverage &beverage) : CondimentDecorator(beverage) {}
virtual std::string GetDescription() const override
{
return this->beverage.GetDescription() + " with Soy";
}
virtual int GetCost() const override
{
return this->beverage.GetCost() + 1;
}
};
int main()
{
Decaf d;
SoyDecorator s(d);
CaramelDecorator c(s);
CaramelDecorator cc(c);
std::cout << cc.GetDescription() << std::endl;
std::cout << cc.GetCost() << std::endl;
}
输出:
Decaf with Soy with Caramel with Caramel
9
Live Demo on coliru
为什么需要额外的候选人?
CondimentDecorator
源自 Beverage
.因此对于:
CondimentDecorator d;
CondimentDecorator d2(d);
编译器有两种选择来构造
d2
:CondimentDecorator::CondimentDecorator(Beverage &beverage)
CondimentDecorator::CondimentDecorator(const CondimentDecorator&)
. 对于第一个,必须应用隐式强制转换,但对于复制构造函数,不需要强制转换(或至多是 const 强制转换)。
因此,编译器更喜欢复制构造函数(不幸的是,即使它被删除了)。
因此,必须提供另一个候选人,它需要像复制构造函数这样的隐式强制转换:
CondimentDecorator::CondimentDecorator(CondimentDecorator&)
. 进一步阅读:Overload Resolution
关于c++ - 为什么装饰模式适用于指针而不适用于引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60943956/