如何在装饰器模式实现中正确使用shared_ptr?

时间:2015-11-22 05:50:57

标签: c++ design-patterns memory-leaks decorator shared-ptr

我在以下代码中遇到内存泄漏问题。我知道有一些流程。但不确定。如何在这些场景中使用shared_ptr?如果我需要添加更多装饰器,比如Chocolate-Pista-Icecream,如何正确传递指针,以便它在出口处被删除?

    class AbstractCream
{
public:
    virtual void ShowFlavour() = 0;
    virtual ~AbstractCream()
    {
        cout << endl << "AbstractCream-DTOR";
    }
};

class IceCream :public AbstractCream
{
public:
    void ShowFlavour()
    {
        cout << "IceCream";
    }
    ~IceCream()
    {
        cout << endl << "IceCream Dtor";
    }
};

class DecoratorCream :public AbstractCream
{
private:
    std::shared_ptr<AbstractCream> AbCream;
public:
    DecoratorCream(std::shared_ptr<AbstractCream>abs) :AbCream(abs)
    {}
    void ShowFlavour()
    {
        AbCream->ShowFlavour();
    }
    virtual ~DecoratorCream()
    {
        cout << endl << "DecoratorCream-DTOR";

    }
};

class ChocolateCream : public DecoratorCream
{
public:
    ChocolateCream(std::shared_ptr<AbstractCream>abs) :DecoratorCream(abs)
    {}
    void ShowFlavour()
    {
        cout << "CholocateCream added..";
        DecoratorCream::ShowFlavour();
    }
    ~ChocolateCream()
    {
        cout << endl << "ChocolateCream-DTOR";
    }

};
class PistaCream : public DecoratorCream
{
public:
    PistaCream(std::shared_ptr<AbstractCream> abs) :DecoratorCream(abs)
    {}
    void ShowFlavour()
    {
        cout << "PistaCream added..";
        DecoratorCream::ShowFlavour();
    }
    ~PistaCream()
    {
        cout << endl << "PistaCream-DTOR";
    }
};

class StrawberryCream : public DecoratorCream
{
public:
    StrawberryCream(std::shared_ptr<AbstractCream> abs) :DecoratorCream(abs)
    {}
    void ShowFlavour()
    {
        cout << "StrawberryCream added..";
        DecoratorCream::ShowFlavour();
    }
    ~StrawberryCream()
    {
        cout << endl << "StrawberryCream-DTOR";
    }
};


int main()
{
    std::shared_ptr <AbstractCream> ice1( new IceCream());
    std::shared_ptr <PistaCream> pista1(new PistaCream(ice1));
    std::shared_ptr <AbstractCream> ice2(new IceCream());
    std::shared_ptr <ChocolateCream>choco1( new ChocolateCream(ice2));

    pista1->ShowFlavour();
    cout << endl;
    choco1->ShowFlavour();
    cout << endl;

    getchar();
    _CrtDumpMemoryLeaks();
    return 0;
}

2 个答案:

答案 0 :(得分:3)

这个问题似乎不是你班级中std::shared_ptr<...>的用法:这似乎在语义上是正确的(但是,要编写的代码太多,不过详细讨论)。相反,我认为您的main()是错误的:您尝试在对象仍然存活的时间点确定内存泄漏。我不是Windows程序,但我非常确定_CrtDumpMemoryLeak()不了解std::shared_ptr<...>,只是简单地报告new内存不存在的问题。 t delete d,但是。

有几种简单的方法可以更改main()以避免此问题:

  1. 将对象分配放入块中并在块后报告内存泄漏:

    int main() {
        {
            std::shared_ptr <AbstractCream> ice1( new IceCream());
            // ...
        }
        _CrtDumpMemoryLeaks();
    }
    
  2. 将执行实际工作的代码放入一个单独的函数中,并在main()中调用此函数并报告内存泄漏:

    int actualMain() {
        std::shared_ptr <AbstractCream> ice1( new IceCream());
        // ...
    }
    int main() {
        int rc = actualMain();
        _CrtDumpMemoryLeaks();
    }
    
  3. 报告早期构建的对象的析构函数中的内存泄漏,例如main()中的第一件事:

    struct Reporter { ~Reporter() { _CrtDumpMemoryLeaks(); } };
    int main() {
        Reporter reporter;
        std::shared_ptr <AbstractCream> ice1( new IceCream());
        // ...
    }
    
  4. 使用所有三种方法,std::shared_ptr<...> s在报告内存泄漏之前被销毁。我非常确定所有这些方法都会使内存泄漏消失。我倾向于使用第三种方法。

    也就是说,从性能的角度来看,std::shared_ptr<...>的传递方式并不理想:每次都会增加引用次数。当它通过多个层时,它会在呼叫时不必要地碰到并且在从呼叫返回时被撞倒。有多种方法可以解决这个问题:

    1. 简单的方法是将std::shared_ptr<...>作为常量引用传递:

      ChocolateCream(std::shared_ptr<AbstractCream> const& abs)
          : DecoratorCream(abs) {
      }
      
    2. 可以认为,通过引用传递会抑制复制省略。但是,参数构造只能在一个级别上省略:当将对象传递给另一个函数时,它是一个命名对象,而复制省略的规则只允许从return和{{1}的命名对象中删除副本}语句。为最内在的构造函数去路线可能仍然是合理的。即使这样,传递throw时也应该移动它(在这种情况下是构造变量的构造):

      std::shared_ptr<...>

      如果你想在其他构造函数中按值传递参数,你至少应该DecoratorCream(std::shared_ptr<AbstractCream> abs) : AbCream(std::move(abs)) { } 参数。这样做应该避免引用计数,但它仍然无法避免所有工作,因为它需要在每个级别上构造/销毁std::move(...)。但是,至少可以避免引用计数的同步维护。

    3. 由于我提到了性能问题:stop using std::endl。它对你没有多大帮助。在你使用它只会减慢程序。

答案 1 :(得分:1)

class AbstractCream
{
public:
    virtual void ShowFlavour() = 0;
    virtual ~AbstractCream()
    {
        cout << endl << "AbstractCream-DTOR";
    }
};

class IceCream :public AbstractCream
{
public:
    void ShowFlavour()
    {
        cout << "IceCream";
    }
    ~IceCream()
    {
        cout << endl << "IceCream Dtor";
    }
};

class DecoratorCream :public AbstractCream
{
private:
    std::shared_ptr<AbstractCream> AbCream;
public:
    DecoratorCream(const std::shared_ptr<AbstractCream> &abs) :AbCream(abs)
    {}
    void ShowFlavour()
    {
        AbCream->ShowFlavour();
    }
    virtual ~DecoratorCream()
    {
        cout << endl << "DecoratorCream-DTOR";

    }
};

class ChocolateCream : public DecoratorCream
{
public:
    ChocolateCream(const std::shared_ptr<AbstractCream>& abs) :DecoratorCream(abs)
    {}
    void ShowFlavour()
    {
        cout << "CholocateCream added..";
        DecoratorCream::ShowFlavour();
    }
    ~ChocolateCream()
    {
        cout << endl << "ChocolateCream-DTOR";
    }

};
class PistaCream : public DecoratorCream
{
public:
    PistaCream(const std::shared_ptr<AbstractCream> &abs) :DecoratorCream(abs)
    {}
    void ShowFlavour()
    {
        cout << "PistaCream added..";
        DecoratorCream::ShowFlavour();
    }
    ~PistaCream()
    {
        cout << endl << "PistaCream-DTOR";
    }
};

class StrawberryCream : public DecoratorCream
{
public:
    StrawberryCream(const std::shared_ptr<AbstractCream>& abs) :DecoratorCream(abs)
    {}
    void ShowFlavour()
    {
        cout << "StrawberryCream added..";
        DecoratorCream::ShowFlavour();
    }
    ~StrawberryCream()
    {
        cout << endl << "StrawberryCream-DTOR";
    }
};


//-------------------dec--------------------------------------------------------------//
struct DummyToLeakCheck
{
public:
    ~DummyToLeakCheck()
    {
        _CrtDumpMemoryLeaks();
    }
};
int main()
{
    DummyToLeakCheck myLeakChecker;
    std::shared_ptr <AbstractCream> ice1( new IceCream());
    std::shared_ptr <PistaCream> pista1(new PistaCream(ice1));
    std::shared_ptr <AbstractCream> ice2(new IceCream());
    std::shared_ptr <ChocolateCream>choco1( new ChocolateCream(ice2));
    std::shared_ptr <StrawberryCream>straw1(new StrawberryCream(choco1));

    pista1->ShowFlavour();
    cout << endl;
    choco1->ShowFlavour();
    cout << endl;
    straw1->ShowFlavour();
    cout << endl;

    getchar();

    return 0;
}

使用第一个答案中提到的泄漏检查器有助于更正原始代码。修改后的代码。到目前为止忽略std :: endl,因为代码的目的是在装饰器模式中尝试智能指针。