【问题标题】:How to implement factory+decorator pattern in c++11如何在 C++11 中实现工厂+装饰器模式
【发布时间】:2015-03-12 08:18:15
【问题描述】:

我决定研究/翻译 Head First Design Patterns 的 Java 代码到 C++11,并且由于智能指针,我能够使用自动内存管理来实现大部分模式。但是,我对其中一个示例有疑问。这是我的代码:

#include <iostream>
#include <memory>

class AbstractBase {
public:
    virtual void foo() = 0;
    virtual ~AbstractBase() = default;
};

class A : public AbstractBase {
public:
    void foo() override { std::cout << "Class A: foo() called" << std::endl; }
};

class B : public AbstractBase {
public:
    void foo() override { std::cout << "Class B: foo() called" << std::endl; }
};

class FooDecorator : public AbstractBase {
public:
    FooDecorator(AbstractBase *pBase): mpBase(pBase) { }
    void foo() override
    {
        mpBase->foo();
        ++mNumberOfFooCalls;
    }
    static int getFooCalls() { return mNumberOfFooCalls; }

private:
    static int mNumberOfFooCalls;
    AbstractBase *mpBase;
};

class AbstractFactory {
public:
    virtual std::unique_ptr<AbstractBase> createA() = 0;
    virtual std::unique_ptr<AbstractBase> createB() = 0;
    virtual ~AbstractFactory() = default;
};

class CountingFactory : public AbstractFactory {
public:
    std::unique_ptr<AbstractBase> createA()
    {
        // auto pA = new A();
        // return std::unique_ptr<AbstractBase>(new FooDecorator(pA));
        std::unique_ptr<AbstractBase> pA(new A());
        return std::unique_ptr<AbstractBase>(new FooDecorator(pA.get()));
    }

    std::unique_ptr<AbstractBase> createB()
    {
        // auto pB = new B();
        // return std::unique_ptr<AbstractBase>(new FooDecorator(pB));
        std::unique_ptr<AbstractBase> pB(new B());
        return std::unique_ptr<AbstractBase>(new FooDecorator(pB.get()));
    }
};

int FooDecorator::mNumberOfFooCalls = 0;

int main()
{
    std::unique_ptr<AbstractFactory> pFactory(new CountingFactory());
    std::unique_ptr<AbstractBase> pObjA = pFactory->createA();
    std::unique_ptr<AbstractBase> pObjB = pFactory->createB();
    pObjA->foo();
    pObjB->foo();
    std::cout << "Foo called " << FooDecorator::getFooCalls()
              << " times." << std::endl;
}

这段代码的本质是;有两个派生类AB;它们每个都有一个成员函数,显示调用了哪个成员函数。还有一个名为FooDecorator 的装饰器,它增加了计算对foo() 的调用的能力。

除了这些,还有CountingFactory,用于直接获取装饰对象。

在主要部分,使用这个工厂,我创建了一个A 的实例和一个B 的实例。然后从每个呼叫foo()

当我使用 clang 3.5 编译此代码并运行它时,我没有收到任何错误,但结果与预期有点不同,因为它调用了两次 B::foo()

Class B: foo() called
Class B: foo() called
Foo called 2 times.

另一方面,当我使用 gcc 4.9.2 编译代码并运行它时,我收到以下错误:

pure virtual method called
terminate called without an active exception

看起来问题出在CountingFactory 中的unique_ptrs。我的理解是用于初始化装饰对象的指针被释放,它导致未定义的行为(clang 情况)或终止(gcc 情况)。

因此,我决定使用原始指针并添加(上面注释掉的)行:

    auto pA = new A();
    return std::unique_ptr<AbstractBase>(new FooDecorator(pA));

    auto pB = new B();
    return std::unique_ptr<AbstractBase>(new FooDecorator(pB));

这样做,事情成功了,我从两个编译器得到了预期的输出。但是,现在存在内存泄漏,必须删除分配。

几乎总能找到解决此类问题的智能指针,我正在努力想出解决此问题的最佳方法。我也尝试将unique_ptrs 替换为shared_ptrs,但无济于事,它不起作用。

我仍然可以采用不同的方法来摆脱智能指针吗?还是我必须手动管理我在工厂内部分配的内存(不是首选)?

【问题讨论】:

  • @IrrationalPerson 很有趣,我得到了上面的结果。可能是因为我的是 Apple 的 clang 3.5。

标签: c++ c++11 design-patterns stl smart-pointers


【解决方案1】:

据我了解,您需要FooDecorator 才能获得pBase 的所有权。你可以通过改变来实现这一点

AbstractBase *mpBase;

std::unique_ptr<AbstractBase> mpBase;

所以你像这样在CountingFactory 中创建FooDecorator

return std::unique_ptr<AbstractBase>(new FooDecorator(new A()));

或者在 C++14 中:

return std::make_unique<FooDecorator>(new A());

【讨论】:

  • 我试图在没有分配的地方坚持使用原始指针,但在这种情况下失败了。因此,教训可能是更多地使用智能指针。非常感谢。
【解决方案2】:

崩溃的原因是你只是通过get方法分配了内部指针。

std::unique_ptr<AbstractBase> pB(new B());
return std::unique_ptr<AbstractBase>(new FooDecorator(pB.get()));

这意味着当作用域结束时,你内心的B(或A)的内存是deleted。 您可以改为拨打release

但是,为了避免泄漏,您的 FooDecorator 中也应该有一个 unique_ptr。

Live on IdeOne

【讨论】:

  • 这能解决问题吗?如果可以,我们可以看到编译后的代码
  • 这应该会运行,但再次将delete指向用户的指针的责任留给了用户。
猜你喜欢
  • 1970-01-01
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-08
  • 2019-03-25
  • 1970-01-01
  • 2020-02-29
相关资源
最近更新 更多