好的,所以如果没有更多代码,这将是非常普遍的。这些是最优先的指南(不是规则)。
首先,关于 C++11 的快速说明:如果您没有它,请在std::unique_ptr
下面替换为std::auto_ptr
(尽管出于某种原因已弃用它,所以要小心),或者改用boost::scoped_ptr。
1.不要使用new
如果您需要创建一个(单个)山并且不需要在声明它的范围之外使其保持活动状态,只需将其用作具有自动范围的常规变量:
void automatic_scope(int size, double seed)
{
Mountain hill(size, Utils::powerOf2Log2(size) - 6, 0.5f, seed);
// ... mountainous operations happen here ...
} // hill is destroyed here - is that ok for you?
同样,如果一座山拥有一个 ChannelClass,它的寿命应该与拥有它的山一样长,只需执行以下操作:
class Mountain
{
ChannelClass channel;
public:
Mountain(int size, int powerthing, double something, double seed)
: channel(size, size) // initialize other members here
{
// any more initialization
}
ChannelClass& toChannel() { return channel; }
};
现在ChannelClass
,只要Mountain
,一切都会自动销毁,并且不需要显式关闭。
2.不要使用new[]
同样,如果您需要几座范围有限的山脉,只需使用
void automatic_scope_vector(int size, double seed)
{
std::vector<Mountain> hills;
hills.push_back(Mountain(size, Utils::powerOf2Log2(size) - 6, 0.5f, seed));
// ... mountainous operations happen here ...
} // hills are all destroyed here
3.好的,new
毕竟使用
显然有使用 new的正当理由:已经提到了一个(你需要让你的山脉比你创建它们的街区更长)。
另一个是如果您需要运行时多态性,例如,如果您有多个 or 的子类Mountain
,ChannelClass
但您想处理基类。
我们可以用多态工厂函数来说明两者:
class Molehill: public Mountain { ... };
class Volcano: public Mountain { ... };
std::unique_ptr<Mountain> make_mountain(int size, double seed, bool is_molehill)
{
std::unique_ptr<Mountain> result;
if (is_molehill)
result.reset(new Molehill(size, size/2, 0.01f, seed));
else
result.reset(new Volcano(size, size*2, 0.5f, seed));
return result;
}
void automatic_scope_polymorphic(int size, double seed, bool is_molehill)
{
std::unique_ptr<Mountain> hill = make_mountain(size, seed, is_molehill);
// ... polymorphic mountainous operations happen here ...
} // hill is destroyed here unless we gave the unique_ptr to someone else
同样,如果ChannelClass
需要动态创建山,请将其存储在unique_ptr
.
在您需要复制对象以传递它们的情况下,有时它也可能会有所帮助,复制非常昂贵,并且您不能依赖(或还没有)RVO 或移动语义。这是一个优化,所以不要担心它,除非分析表明这是一个问题。
哲学
这些 C++ 习惯用法都基于确定性破坏,其目标是完全避免编写显式清理代码。
将内存管理委托给容器(如std::vector
)和智能指针(如std::unique_ptr
)避免了 Java 通过垃圾收集处理的内存泄漏。然而,它有力地推广到RAII,其中类似的自动范围保护对象可以自动管理所有资源,而不仅仅是内存。例如,std::lock_guard
确保即使一个函数有多个返回路径、可能抛出异常等,互斥锁也被正确释放。
如果您确实需要编写显式清理代码:不要编写必须调用的自定义关闭方法,只需将其放在析构函数中即可。如果可能,也将其推送到低级别的保护对象中。