c++ - 为什么装饰模式适用于指针而不适用于引用?

标签 c++ pointers reference decorator

我刚刚了解了装饰器模式,并尝试编写一个使用该代码的示例。这个例子是关于饮料和一些调味品的。在装饰器内部,我有一个 引用变量 到饮料。提供的饮料是DecafEspresso .可用的调味品是SoyCaramel .如果我定义一个 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/

    相关文章:

    c++ - 模板函数静态变量

    C++ 模板编程 - 延迟函数调用

    c++ - 以派生类对象作为参数在基类中声明纯虚函数

    c - 如何将项目扫描到 void* 数组中?

    c++ - 返回指向类的指针的函数

    c - 为什么我用这个方法反向的时候原来的双链被修改了?

    reference - 通过调用 API : local or global? 获取的 JNI 对象引用

    java - Java 中不同类型的引用对象有什么不同吗?

    c++ - 模板特化和DLL:Visual Studio与(GCC/Clang)

    c++ - 使用远程线程的自由库