C ++链表分配运算符问题

时间:2018-11-20 00:52:52

标签: c++ class

我正在编写一个有序链表类定义(OLList)。我已经编写了赋值运算符函数,但是当我尝试通过链接赋值操作对其进行测试时,该程序陷入了OLList::copy函数的while循环中。我知道这是因为我使用控制台打印进行了测试。

//OLList.h
struct Node {
  ListItem item;
  Node *next;
};

class OLList {
  public:
  OLList& OLList::operator =(const OLList& rhs)
  {
    if (this != &rhs) {
        destroy();
        copy(rhs);
    }
    return *this;
  }

  void OLList::destroy()
  {
    Node *current_node = this->headM;
    Node *next_node;
    while(current_node->next != nullptr)
    {
        next_node = current_node->next;
        delete(current_node);
        current_node = next_node;
    }


    return;
  }

void OLList::copy(const OLList& source)
  {
    Node *new_node, *current_node;
    Node *current_source_node = source.headM;
    this->headM->item = source.headM->item;
    current_node = this->headM;

    while(current_source_node->next != nullptr)
    {
        new_node = new(Node);
        current_node->next = new_node;
        current_node = current_node->next;
        current_source_node = current_source_node->next;
        current_node->item = current_source_node->item;
    }

    return;
  }
}

下面是用于测试该类的代码。我已经确保print()函数可以正常工作,所以绝对不是问题。

//main.cpp
int main()
{
    OLList the_list;
    the_list.insert(1);
    the_list.insert(2);

    OLList second_list;
    second_list.insert(3);
    second_list.insert(4);

    OLList third_list;
    third_list.insert(5);
    third_list.insert(6);

    third_list = second_list = the_list;
    third_list.print();
}

当编译并运行该程序时,该程序永远不会终止,因为它陷入了上述循环。

1 个答案:

答案 0 :(得分:1)

如果destroy()headM,则您的nullptr方法将失败。您应该使用while(current_node != nullptr)而不是while(current_node->next != nullptr)。但更重要的是,销毁列表后,它不会将headM重置为nullptr。因此,operator=调用destroy()之后,headM不再处于有效状态,copy()才能使用。

您的copy()方法同样不检查源headM还是目标nullptr。但更重要的是,它假设目标列表事先为空,否则,如果它没有完全崩溃(上),则会泄漏内存。坦率地说,将一个列表复制到另一个列表通常根本没有正确编码。

因此,您的代码正在调用未定义的行为,这任何事情都可能发生。

就像@PaulMcKenzie在评论中指出的那样,您确实应该使用适当的复制构造函数(和析构函数-并且由于您显然使用的是C ++ 11或更高版本,因此也应该使用move构造函数和move赋值运算符-参见Rule of 5)。然后,可以使用复制构造函数来实现您的赋值运算符(对于移动赋值也是如此)。

尝试更多类似的方法:

struct Node {
    ListItem item;
    Node *next = nullptr;

    Node(const ListItem &value) : item(value) {}
};

class OLList {
private:
    Node *headM = nullptr;

public:
    OLList() = default;

    OLList(const OLList &src)
    {
        Node *current_source_node = src.headM;
        Node **current_node = &headM;

        while (current_source_node)
        {
            *current_node = new Node(current_source_node->item);
            current_node = &((*current_node)->next);
            current_source_node = current_source_node->next;
        }

        /* alternatively:
        Node *current_source_node = src.headM;
        while (current_source_node) {
            insert(current_source_node->item);
        }
        */
    }

    OLList(OLList&& src)
    {
        src.swap(*this);
    }

    ~OLList()
    {
        Node *next_node;    

        while (headM)
        {
            next_node = headM->next;
            delete headM;
            headM = next_node;
        }
    }

    void clear() {
        OLList().swap(*this);
    }

    OLList& operator=(const OLList& rhs)
    {
        if (this != &rhs) {
            OLList(rhs).swap(*this);
        }
        return *this;
    }

    OLList& OLList::operator=(OLList&& rhs)
    {
        OLList(std::move(rhs)).swap(*this);
        return *this;
    }

    void swap(OLList &other) {
        std::swap(headM, other.headM);
    }

    void insert(const ListItem &value) {
        ...
    }

    void print() const {
        ...
    }
    ...
};