为啥装饰器模式适用于指针而不适用于引用?
Posted
技术标签:
【中文标题】为啥装饰器模式适用于指针而不适用于引用?【英文标题】:Why Decorator pattern works with pointers but not references?为什么装饰器模式适用于指针而不适用于引用? 【发布时间】:2020-03-31 05:26:42 【问题描述】:我刚刚了解了装饰器模式,并尝试编写一个使用该代码的示例。这个例子是关于饮料和一些调味品的。在装饰器中,我有一个饮料的引用变量。提供的饮料是Decaf
和Espresso
。可用的调味品是Soy
和Caramel
。例如,如果我用多个Caramel
定义一个Decaf
,我得到的结果只是一个带有一个装饰器的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
【问题讨论】:
如果您调用CaramelDecorator cc(c);
,则使用(默认)复制构造函数。您可以删除它以达到您的目的。 (我假设你不需要它。)
【参考方案1】:
通过从指针切换到引用,OP 构造函数签名变得非常类似于(默认)复制构造函数。
CondimentDecorator(Beverage &beverage) : beverage(beverage)
对比
CondimentDecorator(const Beverage&); // generated by compiler
首先,我认为删除复制构造函数就足够了,但编译器仍会尝试使用已删除的构造函数并提出相应的抱怨,因为它不能再使用了。
最后,我能够通过提供响应来解决 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&)
。
首先,必须应用隐式强制转换,但对于复制构造函数,不需要强制转换(或至多,常量强制转换)。
因此,编译器更喜欢复制构造函数(不幸的是,虽然它被删除了)。
因此,必须提供另一个候选者,它需要像复制构造函数这样的隐式强制转换:
-
另一个自定义构造函数
CondimentDecorator::CondimentDecorator(CondimentDecorator&)
。
延伸阅读:Overload Resolution
【讨论】:
以上是关于为啥装饰器模式适用于指针而不适用于引用?的主要内容,如果未能解决你的问题,请参考以下文章