zoukankan      html  css  js  c++  java
  • 八、单链表的实现

    1、链式存储结构线性表的实现:

    LinkList设计要点:类模板

    • 通过头结点访问后继节点
    • 定义内部结点类型Node,用于描述数据域和指针域
    • 实现线性表的关键操作(增、删、改、查等)
    template <typename T>
    class LinkList : public List<T>
    {
    protected:
        struct Node : public Object 
        {
            T value;
            Node* next;
        };
        
        Node m_header;
        int m_length;
        
    public:
        LinkList() {}
    };
    

    具体实现

    template <typename T>
    class LinkList : public List<T>
    {
    protected:
        struct Node : public Object
        {
            T value;    // 数据域
            Node* next; // 指针域
        };
    
        mutable Node m_header;  // 头结点
        int m_length;   // 记录链表长度
    
    public:
        LinkList()
        {
            m_header.next = NULL;
            m_length = 0;
        }
    	
    	// 链表末尾插入元素
        bool insert(const T& e)
        {
            return insert(m_length, e);
        }
        // 指定位置插入元素
        bool insert(int i, const T& e)
        {
            // 注意i的范围
            bool ret = ((i>=0) && (i<=m_length));
            cout << "ret  = " << ret <<  endl;
            if (ret)
            {
                Node* node = new Node();
                if (node != NULL)
                {
                    // current的目标指向其实都是目标位置的前一个,比如:在第0个位置增加元素,current指向的是header
                    Node* current = &m_header;
                    for(int p = 0; p < i; p++)
                    {
                        current = current->next;
                    }
                    node->value = e;
                    node->next = current->next;
                    current->next = node;
    
                    m_length++;
                }
                else
                {
                    cout << "THROW_EXCEPTION" << endl;
                    THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element");
                }
            }
    
            return ret;
        }
    	// 删除指定位置元素
        bool remove(int i)
        {
            // 注意i的范围
            bool ret = ((i>=0) && (i<m_length));
            if (ret)
            {
                Node* current = &m_header;
                for(int p = 0; p < i; p++)
                {
                    current = current->next;
                }
    
                Node* toDel = current->next;
                current->next = toDel->next;
    
                delete toDel;
                m_length--;
            }
            return ret;
        }
        // 设定指定位置的元素
        bool set(int i, const T& e)
        {
            // 注意i的范围
            bool ret = ((i>=0) && (i<m_length));
            if (ret)
            {
                Node* current = &m_header;
                for(int p = 0; p < i; p++)
                {
                    current = current->next;
                }
                current->next->value = e;
            }
            return ret;
        }
        // get函数用起来不方便,重载一下
        T get(int i) const
        {
            T ret;
            if (get(i, ret))
            {
                return ret;
            }
            else
            {
                THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element...");
            }
        }
    	// 获取指定位置的元素
        bool get(int i, T& e) const
        {
            bool ret = ((i>=0) && (i<m_length));
            if (ret)
            {
                Node* current = &m_header;
                for(int p = 0; p < i; p++)
                {
                    current = current->next;
                }
    
                e = current->next->value;
                // get是const成员函数,按理来说不能修改成员变量的值,Node* current=&m_header,会被误认为要更改成员变量的值,故报错
                // 解决方案是对m_header加上mutable,开一个例外
            }
    
            return ret;
        }
        int length() const
        {
            return m_length;
        }
        void clear()
        {
            // 释放每一个结点
            while(m_header.next)
            {
                Node* toDel = m_header.next;
                m_header.next = toDel->next;
                delete toDel;
            }
            m_length = 0;
        }
        ~LinkList()
        {
            clear();
        }
    };
    

    问题:头结点隐患,实现代码优化

    创建m_header时,会调用T value,用泛指类型创建头结点的数据域,当泛指类型为用户自定义类型时,用用户自定义的类类型在库中创建对象,就有可能出错了,而且在外部看来,并没有用该类型创建对象,问题定位很麻烦。

    解决办法:构造头结点时,不调用泛指类型创建头结点,而是按内存分布自己重建构造一个类对象,注意一定要和以前的头结点的内存分布一样,不仅是成员变量的内存大小,同样也要和以前一样继承于Object

    // 直接创建头结点,存在隐患
    mutable Node m_header;
    
    // 重新构造之后的头结点
    mutable struct : public Object
    {
        char reserved[sizeof(T)];   // 没实际作用,占空间
        Node* next;
    } m_header;
    
    class Test
    {
    public:
        Test()
        {
            throw 0;
        }
    };
    int main()
    {
        LinkList<Test> list;
    // 没有重新构造头结点之前,会抛一个异常,是应该用test创建类的时候抛的异常,但是这里应该没有创建test类,使用了test作为类型,创建的LinkList的类对象。出现异常的原因是在用单链表创建类的时候,创建头结点时会调用Test的构造函数,由此引发异常
    
        cout << "sofrware"<< endl;
        // 重新构造之后,以下这样也会报错,但是原因就会是用户自己创建的类对象,方便查找错误
        Test t;
        list.insert(t);
    

    重新构造后的头结点在内存布局上和之前没有差异,差异在于不管泛指类型是什么,都不会去调用泛指类型的构造函数了。虽然它们在内存布局上是一样的,但是新构造的头结点是个空类型,不能直接用,使用时要进行类型转换

    Node* ret = reinterpret_cast<Node*>(&m_header);
    

    优化后的完整代码:

    
    template <typename T>
    class LinkList : public List<T>
    {
    protected:
        struct Node : public Object
        {
            T value;    // 数据域
            Node* next; // 指针域
        };
        // 重新构造头结点
        mutable struct : public Object
        {
            char reserved[sizeof(T)];   // 没实际作用,占空间
            Node* next;
        } m_header;
    
        int m_length;   // 记录链表长度
    	
    	// 位置定位函数,重复使用,进行抽象,方便使用
        Node* position(int i) const
        {
            Node* ret = reinterpret_cast<Node*>(&m_header);
            for(int p = 0; p < i; p++)
            {
                ret = ret->next;
            }
            return ret;
        }
    
    public:
        LinkList()
        {
            m_header.next = NULL;
            m_length = 0;
        }
    
        bool insert(const T& e)
        {
            return insert(m_length, e);
        }
        
        bool insert(int i, const T& e)
        {
            // 注意i的范围
            bool ret = ((i>=0) && (i<=m_length));
            cout << "ret  = " << ret <<  endl;
            if (ret)
            {
                Node* node = new Node();
                if (node != NULL)
                {
                    Node* current = position(i);
                    node->value = e;
                    node->next = current->next;
                    current->next = node;
                    m_length++;
                }
                else
                {
                	THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element");
                }
            }
            return ret;
        }
    
        bool remove(int i)
        {
            // 注意i的范围
            bool ret = ((i>=0) && (i<m_length));
            if (ret)
            {
                Node* current = position(i);
                Node* toDel = current->next;
                current->next = toDel->next;
                delete toDel;
                m_length--;
            }
            return ret;
        }
        bool set(int i, const T& e)
        {
            bool ret = ((i>=0) && (i<m_length));
            if (ret)
            {
                position(i)->next->value = e;
            }
            return ret;
        }
        // get函数用起来不方便,重载一下
        T get(int i) const
        {
            T ret;
            if (get(i, ret))
            {
                return ret;
            }
            else
            {
                THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element...");
            }
        }
    
        bool get(int i, T& e) const
        {
            bool ret = ((i>=0) && (i<m_length));
            if (ret)
            {
                e = position(i)->next->value;
            }
            return ret;
        }
        
        int length() const
        {
            return m_length;
        }
        
        void clear()
        {
            // 释放每一个结点
            while(m_header.next)
            {
                Node* toDel = m_header.next;
                m_header.next = toDel->next;
                delete toDel;
            }
            m_length = 0;
        }
        
        ~LinkList()
        {
            clear();
        }
    };
    

    注意每次代码修改之后都要进行测试,有可能由于修改的代码引入了新的bug

    3、小结

    通过类模板实现链表,包含头结点和长度成员

    定义结点类型,并通过堆中的结点对象构成链式存储

    为了避免构造错误的隐患,头结点类型需要重定义

    代码优化是编码完成后必不可少的环节

  • 相关阅读:
    MS CRM 2011 RC中的新特性(4)——活动方面之批量编辑、自定义活动
    最近的一些有关MS CRM 2011的更新
    MS CRM 2011 RC中的新特性(6)——连接
    MS CRM 2011 RC中的新特性(7)—仪表板
    参加MS CRM2011深度培训课程——第一天
    MS CRM 2011插件调试工具
    MS CRM2011实体介绍(四)——目标管理方面的实体
    MS CRM 2011 RC中的新特性(3)——客户服务管理方面
    MS CRM 2011 RC中的新特性(8)—数据管理
    ExtAspNet 登陆
  • 原文地址:https://www.cnblogs.com/chenke1731/p/9496703.html
Copyright © 2011-2022 走看看