C++:是否必须将策略放入包装器中以避免裸露的新建/删除?
所以我发现了策略的乐趣。
class Strategy
{
public:
virtual void action() = 0;
};
class FooStrategy : public Strategy
{
virtual void action() { /* do some stuff */ };
};
class BarStrategy : public Strategy
{
virtual void action() { /* do different stuff */ };
};
void computation()
{
Strategy *strategy;
if ( /* some decision logic */ )
strategy = new FooStrategy();
else
strategy = new BarStrategy();
//later on...
//big loop! don't want any overheads beyond what if/else would give
for (int i=0;i<100000000;++i)
{
//...various other code and then:
strategy->action();
}
//...other code and possibly more strategy->action() calls, then finally:
delete strategy;
}
所有这些都是为了在每次需要 action()
时用决策逻辑替换 if/else
子句而编写的。我们可以假设它读起来更清楚,因为这就是我们使用它的原因。至于开销,strategy
的虚拟函数表无疑最终会出现在处理器缓存中,因此与 if/else
相比,开销应该很小,对吧?
然而。 C++ 不允许堆栈上的抽象基类,大概是因为它们的大小未知,因此策略必须存在于堆上,从而引入了危险的 new 和 删除
运算符。当然,我可以编写一个包装类来安全地处理策略的创建和删除,并且希望编译器能够完全优化它。但我想知道,如果所有策略都具有相同的大小,是否有一种更优雅的方法将策略放入堆栈中,从而确保自动删除?
编辑感谢大家的回复,我认为他们都非常好,并且对不同的做事方式很有启发。我不会接受任何个人的正确观点。我建议未来的读者考虑所有这些,并慷慨地投票!
So I have discovered the joys of strategies.
class Strategy
{
public:
virtual void action() = 0;
};
class FooStrategy : public Strategy
{
virtual void action() { /* do some stuff */ };
};
class BarStrategy : public Strategy
{
virtual void action() { /* do different stuff */ };
};
void computation()
{
Strategy *strategy;
if ( /* some decision logic */ )
strategy = new FooStrategy();
else
strategy = new BarStrategy();
//later on...
//big loop! don't want any overheads beyond what if/else would give
for (int i=0;i<100000000;++i)
{
//...various other code and then:
strategy->action();
}
//...other code and possibly more strategy->action() calls, then finally:
delete strategy;
}
All this is written to replace an if/else
clause with the decision logic every time I need action()
. We can assume it reads more clearly because that's why we're using it. As to overheads, the virtual function table for strategy
will doubtless end up in the processor cache, so there should be little overhead compared to if/else
, right?
However. C++ doesn't allow abstract base classes on the stack, presumably as their size is unknown, so strategy
has to live on the heap, introducing the dangerous new
and delete
operators. Of course I could write a wrapper class to safely handle the creation and deletion of Strategies, and that would hopefully be optimized away entirely by the compiler. But I wonder if, more elegantly, there is a way to put a Strategy
on the stack, thus ensuring automatic deletion, given that all Strategies have the same size?
Edit Thanks for the replies everyone, I think they're all very good and enlightening as to different ways of doing things. I'm going to abstain from accepting any individual one as correct. I recommend any future readers to consider them all and be liberal with upvotes!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
为什么不直接使用智能指针呢?
现在您无需
删除
任何内容。C++11 还提供
std::unique_ptr
和std::shared_ptr
。此外,Boost 还提供shared_ptr
以及boost::scoped_ptr
(这比auto_ptr
更好,因为复制了auto_ptr
code> 可能会导致问题。scoped_ptr
是不可复制的)。Why not just use a smart pointer?
Now you have no need to
delete
anything.C++11 offers
std::unique_ptr
andstd::shared_ptr
as well. Also, Boost offersshared_ptr
too, as well asboost::scoped_ptr
(which is better thanauto_ptr
since copying anauto_ptr
can cause issues.scoped_ptr
is non-copyable).如果您可以在通话时间决定,您可以使用 templates:
并像这样调用它:
If you can decide that on call time, you could use templates:
and call it like:
您可以按照其他人的建议采用智能指针方式,也可以使用 C 风格的方式:
编辑
或者,以同样的思路。只要您的策略是无状态的,为什么不为每个策略拥有一个全局对象 - 那么您就不必担心删除:
You can go the smart pointer way, as suggested by others, or you can use the C-style way:
Edit
Or, in the same line of thought. As long as your strategies are stateless, why not have a single global object for each strategy - then you wouldn't have to worry about deletion:
这就是我要做的:
private
Strategy
类的智能指针或所有权语义的转移。这应该包含您的if
条件。Strategy
子类都将此函数作为friend
。这样,您将确保您的
Strategy
对象仅作为智能指针的一部分创建。缺点是您需要将friend
声明添加到所有当前和未来的Strategy
子类中。Here's what I would do:
private
Strategy
class with either reference counting or transfer of ownership semantics. This should contain yourif
condition.Strategy
subclasses has this function as afriend
.This way, you will ensure that your
Strategy
objects are only created as parts of a smart pointer. The disadvantage is that you will need to add thefriend
declaration to all current and futureStrategy
subclasses.您可以使用 ScopeGuard 模式的变体:
const 引用(并且必须是 const)可以绑定到临时变量,从而延长临时变量的生命周期。这使您可以安全(高效)地将所有内容放在堆栈上,同时通过其基本类型(作为 const Strategy& )引用对象。
You can use a variant of the ScopeGuard pattern:
A const reference (and it has to be const) can be bound to a temporary, extending the lifetime of the temporary. This allows you to have everything safely (and efficiently) on the stack, while referring to the object by its base type (as a
const Strategy&
).