关于从内存中释放链表的析构函数的逻辑和范围的问题

时间:2014-04-24 18:37:48

标签: c++

我正在处理一个名为Stopwatch的课程(所以请忽略下面的大部分不完整性)。它的界面应该是秒表在现实生活中的一个很好的抽象。现在我正在尝试编写析构函数,它将释放代表所有圈数的链表的内存。

class Stopwatch
{
    typedef enum {UNSTARTED, RUNNING, PAUSED, FINISHED} state;
    typedef struct
    {
        unsigned hours;
        unsigned minutes;
        unsigned seconds;
    } time;
    typedef struct
    {
       unsigned n; // lap number
       time t; // lap time
       lap* next_ptr;
    } lap;

    public: 
    Stopwatch();
    ~Stopwatch();
    void right_button(); // corresponds to start/stop/pause
    void left_button();  // corresponds to lap/reset

    private: 
    state cur_state;
    lap* first_ptr;

}

Stopwatch::Stopwatch()
{
    cur_state = UNSTARTED;
    first_ptr = NULL;
}

Stopwatch::~Stopwatch()
{
    // Destroy all laps
    for (lap* thisptr = first_ptr; thisptr != NULL;)
    {
        lap* tempptr = thisptr; 
        thisptr = thisptr.next_ptr;
        free (tempptr); 
    }
    cur_state = FINISHED;
}

我还没有尝试编译任何东西,但在我继续之前我有几个问题。

(1)我释放链表的逻辑是否正确?我使用的程序是

(i)将thisptr设置为等于第一圈的指针。

(ii)虽然thisptr不是NULL,但是存储thisptr的副本,增加thisptr,然后释放副本指向的内存

这似乎是正确的,但对我来说再次指针仍然很棘手。

(2)我是否应该在使用NULL之后设置等于free的指针?在我到目前为止看到的所有代码示例中,当作者想要删除变量时,他们只是在其上使用free。但我正在阅读这个人的指示http://www.cprogramming.com/tutorial/c/lesson6.html,然后他说要将其设为等于NULL。我一直认为他的教程很好。

(3)当我在析构函数中引用namespace时,是否需要使用lap*运算符?即,我是否需要编写Stopwatch::lap*而不是lap* ???我是否在我班级的正确位置声明了lap结构?

2 个答案:

答案 0 :(得分:1)

释放后将指针设置为NULL是没有必要的。可以建议如果在free()之后有用,代码将(应该......)立即崩溃并且更容易调试。 但是在C ++中你不应该需要它,因为你应该使用RAII并且永远不会拥有所有权的原始指针。

另请注意,在C ++中,您使用的是newdelete,而不是mallocfree

你的循环不是很惯用,它看起来更像是一个while循环,因此它更具可读性:

lap* thisptr = first_ptr;
while(thisptr)
{
    lap* tempptr = thisptr; 
    thisptr = thisptr.next_ptr;
    free (tempptr); 
}

逻辑似乎没问题。但是,如果不是一些家庭作业项目,你应该改变一些事情:

1)使用标准容器。如果你可以使用vector,那就用它吧。如果您的代码不适合矢量,请重新考虑使用矢量;-) 如果您以这种方式更改代码,则不需要析构函数:

class Stopwatch
{
    ...
    typedef struct
    {
       unsigned n; // lap number
       time t; // lap time
    } lap;

    ...

    private: 
    ...
    std::list<lap> laps; // Could you use vector?

}

注意,在C ++中,您通常以这种方式声明结构:

    struct lap
    {
       unsigned n; // lap number
       time t; // lap time
    };

你可以在秒表课程中使用lap来引用它。

2)C ++ 11提供日期/时间实用程序:

class Stopwatch
{
    ...
    typedef std::chrono::system_clock::time_point time;
    ...
}

3)最好在构造函数中使用初始化列表:

Stopwatch::Stopwatch()
  : cur_state(UNSTARTED)
{
}

对于C ++ 11中的简单案例,你甚至不需要构造函数:

class Stopwatch
{
    ...
    private: 
    state cur_state = UNSTARTED;
    ...
}

另请注意,在析构函数中将状态更改为FINISHED几乎没用,因为对象已被销毁。

答案 1 :(得分:0)

1)逻辑对我来说很好。我不能保证它会在没有尝试的情况下发挥作用,但这个想法是正确的。

2)如果指针对其他代码可见,最好在freedelete之后将其设置为null,这样每个人都会理解它不再指向任何有效的东西。但是,如果你是一个破坏者,无论如何一切都将要消失,那真的并不重要。但是,如果left_button中的重置功能实际上释放了列表,那么你需要在结束时将first_ptr设置为null。

3)不,当你在里面时,你不需要明确地扩展到你自己的班级。