我在以下代码中遇到内存泄漏问题。我知道有一些流程。但不确定。如何在这些场景中使用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;
}
答案 0 :(得分:3)
这个问题似乎不是你班级中std::shared_ptr<...>
的用法:这似乎在语义上是正确的(但是,要编写的代码太多,不过详细讨论)。相反,我认为您的main()
是错误的:您尝试在对象仍然存活的时间点确定内存泄漏。我不是Windows程序,但我非常确定_CrtDumpMemoryLeak()
不了解std::shared_ptr<...>
,只是简单地报告new
内存不存在的问题。 t delete
d,但是。
有几种简单的方法可以更改main()
以避免此问题:
将对象分配放入块中并在块后报告内存泄漏:
int main() {
{
std::shared_ptr <AbstractCream> ice1( new IceCream());
// ...
}
_CrtDumpMemoryLeaks();
}
将执行实际工作的代码放入一个单独的函数中,并在main()
中调用此函数并报告内存泄漏:
int actualMain() {
std::shared_ptr <AbstractCream> ice1( new IceCream());
// ...
}
int main() {
int rc = actualMain();
_CrtDumpMemoryLeaks();
}
报告早期构建的对象的析构函数中的内存泄漏,例如main()
中的第一件事:
struct Reporter { ~Reporter() { _CrtDumpMemoryLeaks(); } };
int main() {
Reporter reporter;
std::shared_ptr <AbstractCream> ice1( new IceCream());
// ...
}
使用所有三种方法,std::shared_ptr<...>
s在报告内存泄漏之前被销毁。我非常确定所有这些方法都会使内存泄漏消失。我倾向于使用第三种方法。
也就是说,从性能的角度来看,std::shared_ptr<...>
的传递方式并不理想:每次都会增加引用次数。当它通过多个层时,它会在呼叫时不必要地碰到并且在从呼叫返回时被撞倒。有多种方法可以解决这个问题:
简单的方法是将std::shared_ptr<...>
作为常量引用传递:
ChocolateCream(std::shared_ptr<AbstractCream> const& abs)
: DecoratorCream(abs) {
}
可以认为,通过引用传递会抑制复制省略。但是,参数构造只能在一个级别上省略:当将对象传递给另一个函数时,它是一个命名对象,而复制省略的规则只允许从return
和{{1}的命名对象中删除副本}语句。为最内在的构造函数去路线可能仍然是合理的。即使这样,传递throw
时也应该移动它(在这种情况下是构造变量的构造):
std::shared_ptr<...>
如果你想在其他构造函数中按值传递参数,你至少应该DecoratorCream(std::shared_ptr<AbstractCream> abs)
: AbCream(std::move(abs)) {
}
参数。这样做应该避免引用计数,但它仍然无法避免所有工作,因为它需要在每个级别上构造/销毁std::move(...)
。但是,至少可以避免引用计数的同步维护。
由于我提到了性能问题: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,因为代码的目的是在装饰器模式中尝试智能指针。