【问题标题】:Implementation of a Doubly Linked List双向链表的实现
【发布时间】:2018-03-14 22:51:13
【问题描述】:

我正在尝试实现一个链表,但我完全迷失了。我到处都是断点,特别是使用擦除方法。每当我改变擦除方法时,不可避免地会出现一些错误。我遇到了指针错误、仅在调用擦除方法时出现的析构函数问题等等。

这是我目前所拥有的:

头文件:

#pragma once

class IntList {
private:

    class IntNode {
    public:
        IntNode(int v, IntNode *pr, IntNode *nx);
        ~IntNode();
        IntNode* previous;
        IntNode* next;

        class iterator {

        public:
            iterator(IntNode* t);
            int& operator*();
            iterator& operator++();
            iterator& operator--();
            bool operator!=(iterator other)const;
        private:
            IntNode* target;
        };

    private:
        int value;
    };

    IntNode* head;
    IntNode* tail;
    int count;

public:

    IntList();
    ~IntList();
    void push_back(int v);
    void pop_back();
    int size() const { return count; }
    typedef IntNode::iterator iterator;
    iterator begin();
    iterator end();
    //unsigned int size() const;
    void push_front(int value);
    bool empty() const;
    int& front();
    int& back();
    void clear();
    iterator erase(iterator position);
};

实施:

#include "IntList.h"
#include <stdexcept>

IntList::IntList() : head{ nullptr }, tail{ nullptr }, count{ 0 }
{}

IntList::~IntList() {
    while (head) {
        head = head->next;
        delete head;
    }
}

void IntList::push_back(int v) {
    tail = new IntNode{ v, tail, nullptr };
    if (!head) { head = tail; }
    count += 1;
}

void IntList::pop_back() {
    tail = tail->previous;
    delete tail->next;
    count -= 1;
}

IntList::iterator IntList::begin()
{
    return iterator{ head };
}

IntList::iterator IntList::end() {
    return iterator{ nullptr };
}

void IntList::push_front(int value) {
    head = new IntNode{ value, nullptr, head };
    if (!tail) { tail = head; }
    count += 1;
}

bool IntList::empty() const{
    return (count==0);
}

int& IntList::front() {
    return *begin();
}

int& IntList::back() {
    return *begin();
}

void IntList::clear() {
    head = nullptr;
    tail = nullptr;
    count = 0;
}

IntList::iterator IntList::erase(iterator position) {

    int midpointL = 0;

    for (iterator index = begin(); index != position; ++index) {
        midpointL++;
    }

    if (midpointL == 0) {
        head = head->next;
    }
    else if (midpointL == count) {
        tail = tail->previous;
    }
    else {

        // Move head to get a reference to the component that needs to be deleted
        for (int i = 0; i < midpointL; i++) {
            head = head->next;
        }

        // Change the previous and next pointers to point to each other
        (head->previous)->next = (head->next);
        (head->next)->previous = (head->previous);

        for (int i = midpointL-1; i > 0; i++) {
            head = head->previous;
        }

    }

    count-=1;

    return position;
}


IntList::IntNode::IntNode(int v, IntNode * pr, IntNode * nx)
    : previous{ pr }, next{ nx }, value{ v }
{
    if (previous) { previous->next = this; }
    if (next) { next->previous = this; }
}

IntList::IntNode::~IntNode() {
    if (previous) previous->next = next;
    if (next) next->previous = previous;
}

IntList::IntNode::iterator::iterator(IntNode* t)
    : target{ t }
{}

int& IntList::IntNode::iterator::operator*() {
    if (!target) { throw std::runtime_error{ "Deferenced sentinel iterator." }; }
    return target->value;
}

IntList::IntNode::iterator& IntList::IntNode::iterator::operator++()
{
    if (target) { target = target->next; }
    return *this;
}

IntList::IntNode::iterator& IntList::IntNode::iterator::operator--()
{
    if (target) { target = target->previous; }
    return *this;
}

bool IntList::IntNode::iterator::operator!=(iterator other)const
{
    return (!(target == other.target));
}

谁能帮我指出正确的方向?

谢谢!

【问题讨论】:

  • 1) std::list 已经存在。 2) 您可能真的只想使用std::vector 代替(在现实生活中,链表是一种性能极差的可怕数据结构)。
  • 我认为他这样做是为了练习,链接列表也有自己的用法,例如当您需要从列表中进行恒定时间插入/删除时(例如在时间可预测性为绝对关键)
  • "int& IntList::back() { return begin(); }" - 看起来*错了。为什么你会end() 返回begin()?这没有任何意义。
  • @BlooB 研究方面是有效的(但 OP 可以提到他这样做是出于学习目的)。 Big O 论点在现实生活中并不真正成立——现代 CPU 真的不喜欢在内存中追逐指针(这是你在寻找插入点时要做的事情)——a vector 对预取器要友好得多。当然,理论上列表的插入速度更快,但在实践中; vector 每次都击败他们。
  • 我同意@JesperJuhl 的评估,但在低端微控制器上,没有现代处理器的所有花里胡哨,链表还不错,vector 可以是等待发生的内存碎片死亡事件。请注意,在这个世界上,您预先分配了所有列表节点,当您用完时......好吧,您做什么取决于您碰巧需要该节点。

标签: c++ pointers linked-list iterator


【解决方案1】:

让我们在这里快速回顾一下:

IntList::~IntList() {
    while (head) {
        head = head->next;
        delete head;
    }
}

你应该这样做:

IntList::~IntList() {
    while (head) {
        IntNode* newHead = head->next;
        delete head;
        head = newHead;
    }
}

因为您正在删除“下一个”对象,然后您尝试在下一次迭代中访问它。

void IntList::pop_back() {
    tail = tail->previous;
    delete tail->next;
    count -= 1;
}

这里你没有检查tail是否为空或者它是否指向head..(空的条件是什么?),也许count!=0?以防您可能删除不存在的下一个节点

IntList::iterator IntList::end() {
    return iterator{ nullptr };
}

..end 为空? ebd 应该是你的尾巴……

int& IntList::back() {
    return *begin();
}

开始了..不是回来了。

void IntList::clear() {
    head = nullptr;
    tail = nullptr;
    count = 0;
}

清除应该释放列表中的所有对象。你在这里产生垃圾(泄漏)。

我停在这里,很抱歉,这只是一个咖啡休息时间。但是你应该仔细看看: * 空指针使用 * 在不需要时删除您的节点列表项 * 注意不要使用无效指针(比如head-&gt;previous-&gt;next我在某处看到的)

您必须自下而上地审查您的代码。希望这些最初的提示能帮助您完成学习过程。

玩得开心, 步骤

【讨论】:

    猜你喜欢
    • 2021-11-18
    • 2011-06-03
    • 2012-05-10
    • 2013-02-22
    • 2017-06-10
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    相关资源
    最近更新 更多