C ++ ---如何编写正确的代码来释放本例中分配的内存?

时间:2010-11-09 20:43:22

标签: c++

基于此http://sourcemaking.com/design_patterns/command/cpp/1, 我认为发布的代码有内存泄漏b / c从未发布存储在数组中的已分配内存。我做了如下修改,

#include "stdafx.h"
#include <iostream>
using namespace std;

class Giant
{
public:
    Giant()
    {
        m_id = s_next++;
    }
    void fee()
    {
        cout << m_id << "-fee  ";
    }
    void phi()
    {
        cout << m_id << "-phi  ";
    }
    void pheaux()
    {
        cout << m_id << "-pheaux  ";
    }
private:
    int m_id;
    static int s_next;
};
int Giant::s_next = 0;

// Define a Command interface with a method signature like execute().
class Command
{
public:
    typedef void(Giant:: *Action)();
    Command(Giant *object, Action method)
    {
        m_object = object;
        m_method = method;
    }
    void execute()
    {
        (m_object->*m_method)();
    }
private:
    Giant *m_object;
    Action m_method;
};

template <typename T> class Queue
{
public:
    Queue()
    {
        m_add = m_remove = 0;
    }
    void enque(T *c)
    {
        m_array[m_add] = c;
        m_add = (m_add + 1) % SIZE;
    }
    T *deque()
    {
        int temp = m_remove;
        m_remove = (m_remove + 1) % SIZE;
        return m_array[temp];
    }
private:
    enum
    {
        SIZE = 8
    };
    T *m_array[SIZE];
    int m_add, m_remove;
};

int main()
{
    Queue<Command> que;
    Command *input[] = 
    {
        new Command(new Giant, &Giant::fee), 
        new Command(new Giant, &Giant::phi),
        new Command(new Giant, &Giant::pheaux), 
        new Command(new Giant, &Giant::fee), 
        new Command(new Giant, &Giant::phi), 
        new Command(new Giant, &Giant::pheaux)
    };

    // 24 / 4 = 6
    size_t arrSize = sizeof(input)/sizeof(input[0]);

    for (int i = 0; i < 6; i++)
        que.enque(input[i]);

    for (int i = 0; i < 6; i++)
        que.deque()->execute();
    cout << '\n';

    // I add the following code to release the memory, however I am not sure
    // whether o not this is enough
    // There are two memory allocations in this code
    // 1-> new command and 2->new Giant
    // I think my code only release the memory allocated by new command.
    // Is this correct?
    for (int i = 0; i < 6; i++)
    {
        delete input[i];
        input[i] = NULL;
    }
}

请在上面提出我的问题。

谢谢

6 个答案:

答案 0 :(得分:4)

  

我认为我的代码只释放新命令分配的内存。这是对的吗?

是的,这是对的。但...

为什么你甚至不打算在堆上分配那些?只需在堆栈上创建它们,并在需要的地方传递指针。那么你就没有机会忘记解除分配。

Giant giant;
std::vector<Command> input;
input.push_back(Command(&giant, &Giant::fee));
input.push_back(Command(&giant, &Giant::phi));
input.push_back(Command(&giant, &Giant::pheaux));
input.push_back(Command(&giant, &Giant::fee));
input.push_back(Command(&giant, &Giant::phi));
input.push_back(Command(&giant, &Giant::pheaux));

// ...

for (int i = 0; i < input.size(); i++)
    que.enque(&input[i]);

你应该查看RAII成语。在C ++中将new放在构造函数之外的任何地方是不好的做法(除非你将直接传递给智能指针),并且将delete置于任何地方是不好的做法在析构函数之外。尝试一次分配多个东西也是不好的,不用在RAII或智能指针中分别包装每个东西。

您可以使代码能够正常工作,而不使用RAII /智能指针。但是在保证在任何情况下都没有内存泄漏的情况下做起来是非常困难的,特别是如果在代码中的任何地方抛出异常。

此外,您可以使用现有的队列数据结构,而不是滚动自己的队列数据结构。我强烈建议您避免滚动自己的数据结构,除非您真的需要,或者您专门尝试了解数据结构如何在内部工作(并且您没有将该代码投入生产)。这段代码似乎是成员函数指针的演示,所以我认为队列实现是偶然的复杂性。

改为使用std::queue

答案 1 :(得分:1)

您是对的 - delete input[i]只会删除Command的实例,而不会删除Giant的实例。

答案 2 :(得分:1)

“我认为我的代码只释放新命令分配的内存。这是正确的吗?”

是的,如果你的意思是我的意思。 Command实例已取消分配。但是每个Command实例都包含一个指向动态分配的Giant的指针,该指针未被释放。

这个特定程序的一个简单方法是不使用动态分配。

更一般地说,使用std::queue等容器类和std::auto_ptr之类的智能指针。

干杯&amp;第h。,

答案 3 :(得分:1)

是的,你会泄漏所有那些巨人。对于一个小测试,我只是把它们放在堆栈而不是牛羚的方法:

    Queue<Command> que;
    Giant giant;
    Command input[] = 
    {
        Command(&giant, &Giant::fee), 
        Command(&giant, &Giant::phi),
        Command(&giant, &Giant::pheaux), 
        Command(&giant, &Giant::fee), 
        Command(&giant, &Giant::phi), 
        Command(&giant, &Giant::pheaux)
    };

    // 24 / 4 = 6
    size_t arrSize = sizeof(input)/sizeof(input[0]);

    for (int i = 0; i < arrSize; i++)
        que.enque(input+i);

答案 4 :(得分:0)

你是对的。有泄漏。

答案 5 :(得分:-1)

您需要使用智能指针来表明所有权:

我个人会将Queue对象用作所有者 但是还有其他选择(您的解决方案可能非常基于您尚未阐明的背景)。

例如,对象input可以获得所有权。但是你需要将它从一个简单的数组转换为理解所有权的东西,从而具有构造函数/析构函数。

以下步骤是我在考虑获取所有权的Queue对象。但是同样的原则将适用于任何拥有所有权的对象。

第1步:

确保Queue获得所有权并整理

template <typename T> class Queue
{
    // This object takes ownership so make sure the it tides itself up:

    // Options 1: Use an array of smart pointer:
    std::auto_ptr<T> m_array[SIZE];  // Note using auto_ptr will make the object uncopyable.

    // Options 2: Use a smarter pointer
    boost::shared_ptr<T> m_array[SIZE];

    // Option 3: Use a smart container
    boost::ptr_vector<T> m_array; // Initialize in the destructor to SIZE

    // Option 4: Manage the array yourself.
    private: Queue(Queue const& copy);            // don't define
    private: Queue& operator=(Queue const& copy); // don't define
    public: ~Queue()  { /* Loop over m_array and call delete on each member */ }
};

第2步:

你可以管理队列中的指针。您应该使接受指针的接口显式。

void enque(std::auto_ptr<T> c) // This explicitly tells the caller that you are
                               // taking ownership and they should not try and manage
                               // object after you receive it.

步骤3:
决定deque实际上做了什么 它会释放所有权吗?在这种情况下,你应该返回一个智能指针 它是否给予共享所有权?在这种情况下,您将需要一个共享指针 它是否为应用程序中的简单usge提供了一个对象?返回参考。