0

我无法解决我的小程序中的内存泄漏问题。一些代码最初是用 Java 创建的,所以我将它“转换”为 C++(其中一些事情可能看起来很奇怪,所以如果你有更好的解决方案,请告诉我——我对 C++ 中的 OOP 很陌生)。我的目的是创建一个随机高度图生成器。有 2 个内存泄漏(使用 Visual Leak Detector 发现):

第一个在这里触发:

-> Mountain* mount = new Mountain(size, Utils::powerOf2Log2(size) - 6, 0.5f, seed);
   ChannelClass* height = mount->toChannel();

因此在“Mountain”类构造函数中:

channel = new ChannelClass(size, size);

我试图使用这样的关闭方法:

mount->ShutDown();
delete mount;
mount = 0;

使用 Shutdown() 定义如下:

if(channel){
    channel->ShutDown();
    delete channel;
    channel = 0;
}

“ChannelClass”的 ShutDown() 方法正在删除一个浮点数组。我最初的想法是,“ChannelClass* height = mount->toChannel()”可能会在那里引起问题。

如果您需要更多代码,请告诉我!提前感谢任何愿意提供帮助的人!

4

1 回答 1

6

好的,所以如果没有更多代码,这将是非常普遍的。这些是最优先的指南(不是规则)。

首先,关于 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 的子类MountainChannelClass但您想处理基类。

我们可以用多态工厂函数来说明两者:

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确保即使一个函数有多个返回路径、可能抛出异常等,互斥锁也被正确释放。

如果您确实需要编写显式清理代码:不要编写必须调用的自定义关闭方法,只需将其放在析构函数中即可。如果可能,也将其推送到低级别的保护对象中。

于 2012-08-21T18:33:58.603 回答