Achieve static single linked list

 

Usage scenarios:
Frequent deletions data elements
the maximum number of data elements in a fixed

 

A single list of defects
- triggers
  prolonged use of a single list objects frequently add and delete data elements
- likely outcome
  heap space a large amount of memory fragmentation, cause the system to run slowly

Explanation:

Whenever adding a single data element to the list and they will create a node junction to heap space to do so without any problems from the procedural point of view, but for a long time frequent create and delete node junction in some systems point, it is possible to produce large amounts of heap memory fragmentation. With memory fragmentation, the direct result is that the system is running slow. This is a flaw singly-linked list implementation.

The new linear form
design ideas:
increase the space reserved inside a single list of all Node objects in this space in the dynamic creation and destruction of dynamic
order form single list + = static single list

Inheritance hierarchy static single list

 

Static single chain addition to the different ways of memory, single chain and other operations is exactly the same prior to implementation.

Thinking static single linked list implemented
through the template defines a single static member objectlist (StaticLinkList)
defined in the class in a fixed-size space (unsigned char [])
rewriting create and destroy functions, changing the way memory allocation and restitution
weight in the Node class upload operator new, to create objects in the specified memory

Static single linked list to achieve:

Look at how Node LinkList.h in this type is defined.

struct Node : public Object

{

  T value; //既然是泛指类型的value,那么这个value就有可能是一个对象(用户自定义类类型的对象),必然牵涉到构造函数调用的问题了。value构造函数的调用,必然牵涉到Node构造函数的调用了(value是Node的一个成员变量)

  Node* next;

};

在StaticLinkList.h中,再看一下create函数的定义,

Node* create()

    {

        Node* ret = NULL;

        for(int i=0; i<N; i++)  //首先遍历空间哪些是可用的,就是通过这个标记数组来判断。

        {

            if(!m_used[i])

            {

                ret = reinterpret_cast<SNode*>(m_space) + i; //首先做强制类型转换,再做指针运算。内存分配好了之后,还需要在指定的内存上调用构造函数,因此需要重载new操作符。

                m_used[i] = 1;

                break;

            }

        }

        return ret;

    }

这里面不涉及Node构造函数的定义。仅仅是单纯的分配内存,并没有涉及到构造函数的调用。

分配好内存之后,我们还需要在指定的内存上调用构造函数。此时就需要重载new操作符了。

struct SNode : public Node

{

  //loc保存了一个内存地址,该内存地址就是调用构造函数的内存地址

  void* operator new(unsigned int size, void* loc)

  {

    return loc;

  }

};

Node* create()

    {

        SNode* ret = NULL;

        for(int i=0; i<N; i++)  //首先遍历空间哪些是可用的,就是通过这个标记数组来判断。

        {

            if(!m_used[i])

            {

                ret = reinterpret_cast<SNode*>(m_space) + i; //首先做强制类型转换,再做指针运算。内存分配好了之后,还需要在指定的内存上调用构造函数,因此需要重载new操作符。

                ret = new(ret) SNode();

                m_used[i] = 1;

                break;

            }

        }

        return ret;

    }

void destroy(Node* pn)

{

  SNode* space = reinterpret_cast<SNode*>(m_space);

  SNode* psn = dynamic_cast<SNode*>(pn);//从父类指针,转换为子类指针,直接调用dynameic_cast

 

  for(int i=0; i<N; i++)

  {

    if(psn == (space + i))

    {

      m_used[i] = 0;

      psn->~SNode();

    }

  }

}

具体代码如下:

#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H

#include "linklist.h"

namespace DTLib
{

template <typename T,int N> //N用来定义预留大小空间使用的
class StaticLinkList : public LinkList<T>
{
protected:
    typedef typename LinkList<T>::Node Node; //为什么需要加上typename,因为LinkList<T>::Node这种写法,根本就无法确定Node是一种类型还是静态成员变量。

    struct SNode : public Node
    {
        //loc保存了一个内存地址,该内存地址就是调用构造函数的内存地址
        void* operator new(unsigned int size, void* loc)
        {
            (void)size;
            return loc;
        }
    };

    unsigned char m_space[sizeof(SNode) * N]; //定义预留的空间
      int m_used[N]; //标记数组

    Node* create()
    {
        SNode* ret = NULL;

        for(int i=0; i<N; i++)  //首先遍历空间哪些是可用的,就是通过这个标记数组来判断。
        {
            if(!m_used[i])
            {
                ret = reinterpret_cast<SNode*>(m_space) + i; //首先做强制类型转换,再做指针运算。内存分配好了之后,还需要在指定的内存上调用构造函数,因此需要重载new操作符。
                       ret = new(ret) SNode();
                m_used[i] = 1;
                break;
            }
        }
        return ret;
    }

    void destroy(Node* pn)
    {
        SNode* space = reinterpret_cast<SNode*>(m_space);
        SNode* psn = dynamic_cast<SNode*>(pn);//从父类指针,转换为子类指针,直接调用dynameic_cast

          for(int i=0; i<N; i++)
        {
            if(psn == (space + i))
            {
                m_used[i] = 0;
                psn->~SNode();
            }
        }
    }

public:
    StaticLinkList()
    {
        for(int i=0; i<N; i++)
        {
            m_used[i] = 0;
        }
    }

    int capacity()
    {
        return N; //表示静态单链表最多可以容纳多少个数据元素
    }
};

}

#endif // STATICLINKLIST_H

LinkList中封装create和destroy函数的意义:

为静态单链表(StackLinkList)的实现做准备。StaticLinkList与LinkList的不同仅在于链表结点内存分配上的不同。因此将仅有的不同封装于父类和子类的虚函数中

小结:

顺序表与单链表相结合后衍生出静态单链表

静态单链表是LinkList的子类,拥有单链表的所有操作

静态单链表在预留的空间中创建结点对象

静态单链表适合于频繁增删数据元素的场合(最大元素个数固定)

 

Guess you like

Origin www.cnblogs.com/-glb/p/12147366.html