c ++中的通用链表

时间:2011-06-28 15:03:54

标签: c++ struct linked-list

我现在已经苦苦挣扎了很长一段时间,并提出了一个关于如何在c ++中创建通用链表的相当简单的问题。该列表应该包含几种类型的结构,但每个列表只包含一种类型的结构。当我想实现getNode()函数[见下面]时出现问题,因为那时我必须指定它应该返回哪个结构。我试图用类替换结构,其中getNode函数返回一个由所有其他类继承的基类,但它仍然没有做到这一点,因为编译器不允许getNode函数返回除了基类然后。

所以这里有一些代码片段:

typedef struct struct1 
{
    int param1;
(...)
} struct1;

typedef struct struct2 
{
    double param1;
(...)
} struct2;


typedef struct node
{
    struct1 data;
    node* link;
} node;

class LinkedList
{
public:
    node *first;
    int nbrOfNodes;
    LinkedList();
    void addNode(struct1);
    struct1 getNode();
    bool isEmpty();
};

LinkedList::LinkedList()
{
    first = NULL;
    nbrOfNodes = 0;
}

void LinkedList::addNode(struct1 newData)
{
    if (nbrOfNodes == 0)
    {
        first = new node;
        first->data = newData;
    }
    else
    {
        node *it = first;
        for (int i = 0; i < nbrOfNodes; i++)
        {
            it = it->link;
        }
        node *newNode = new node;
        newNode->data = newData;
        it->link = newNode;
    }
    nbrOfNodes++;
}

bool LinkedList::isEmpty()
{
    return !nbrOfNodes;
}

struct1 LinkedList::getNode()
{
    param1 returnData = first->data;
    node* deleteNode = first;
    nbrOfNodes--;
    if (nbrOfNodes)
        first = deleteNode->link;
    delete deleteNode;
    return returnData;
}

所以问题,用一句话来说,如下:我如何调整上面的链表类,以便它也可以用于struct2,而不必为struct2对象创建一个新的几乎相同的列表类?如上所述,LinkedList的每个实例只处理struct1或struct2。 感谢提示或帮助

5 个答案:

答案 0 :(得分:10)

C ++中已有一个通用链接列表,std::list。它肯定会更有效率和应该足以满足您的使用。

如果您仍想创建自己的通用链接列表 您应该考虑使用 templates 并创建链接列表的模板实现。

在c中,模板不可用时,数据节点以void*指针的形式存储。它利用了void指针可以指向任何通用数据类型的事实,您也可以考虑这种方法。

答案 1 :(得分:1)

基本的tempaltes很容易。

只需使用模板化类型变量将类声明为模板 现在,除了你想要通用的声明类型之外,在类中,用显示的变量名替换显式类型名。

例如,在您的代码中,您希望 struct1 是通用的,因此我们将其替换为 T

template<class T>
class LinkedList { 
    public:     
    node *first;     
    int nbrOfNodes;     LinkedList();     
    void addNode(T);     
    T getNode();     
    bool isEmpty(); 

}; 

答案 2 :(得分:1)

这是一个不使用 STL 的通用实现。您可以创建具有泛型类型的模板化类并实例化 singly_linked_list

namespace api
{
    template <typename T>
    class i_list
    {
    public:

        i_list() = default;

        virtual ~i_list(){}

        /* Add to front*/
        virtual int push_front(T t_value) = 0;

        /* Return the front item*/
        virtual T top_front() = 0;

        /* Remove and return the front elenment */
        virtual T pop_front() = 0;

        /* Add to the back */
        virtual int push_back(T t_value) = 0;

        /* Returns the back item */
        virtual T top_back() = 0;

        /* Removes and returns the back item  */
        virtual T pop_back() = 0;

        /* Is key in the list  */
        virtual bool find(T t_value) = 0;

        /* Remove the key from the list */
        virtual int erase(T t_value) = 0;

        /* Erases the entire list  */
        virtual void erase_all() = 0;

        /* Is the list empty */
        virtual bool empty() = 0;

        /* return the size of the list */
        virtual size_t size() = 0;

    };
}

namespace list
{
    template<typename T>
    struct node
    {
        T m_data;
        
        node* m_next;

        node* m_jump;

        int m_order;

        node(T t_data) : m_data(t_data), m_next(nullptr) , m_jump(nullptr), m_order(-1){}
    };

    template<typename T>
    class singly_linked_list : public api::i_list<T>
    {
    public:

        singly_linked_list() : m_head(nullptr), m_size(0){}

        ~singly_linked_list() {erase_all();}
        
        virtual int push_front(T t_value) override;

        virtual T top_front() override;

        virtual T pop_front() override;

        virtual int push_back(T t_value) override;

        virtual T top_back() override;

        virtual T pop_back() override;

        virtual bool find(T t_value) override;

        virtual int erase(T t_value) override;

        virtual void erase_all() override;

        virtual bool empty() override;

        virtual size_t size() override;
        
        template<typename U>
        friend node<U>* get_head(singly_linked_list<U>& t_list);
        
    private:

        node<T>* get_last_node();

        node<T>* get_node_until(size_t t_index);

        node<T>* find_node(T t_value);

        node<T>* get_node_pointer(size_t t_position);

        int find_node_index(T t_value);

        singly_linked_list<T> get_all_addresses();

        node<T>* m_head = nullptr;

        size_t m_size;
    };

    /* O(1) */
    template<typename T>
    inline int singly_linked_list<T>::push_front(T t_value)
    {
        node<T>* new_node = new node<T>(t_value);
    
        new_node->m_next = m_head;
    
        m_head = new_node;

        m_size++;
    
        return 0;
    }
    
    /* O(1) */
    template<typename T>
    inline T singly_linked_list<T>::top_front()
    {
        if (empty())
        {
            std::cout << " list is empty " << std::endl;
            return T();
        }
    
        return m_head->m_data;
    }
    
    /* O(1) */
    template<typename T>
    inline T singly_linked_list<T>::pop_front()
    {
        if (empty())
        {
            std::cout << " list is empty " << std::endl;
            return T();
        }
    
        /* Value to be returned */
        T value = m_head->m_data;
    
        node<T>* next_node = m_head->m_next;
    
        delete(m_head);
    
        m_head = next_node;

        m_size--;

        return value;
    
    }
    
    /* O(N) */
    template<typename T>
    inline int singly_linked_list<T>::push_back(T t_value)
    {
        node<T>* new_node = new node<T>(t_value);

        if (empty())
        {
            m_head = new_node;

            m_size++;

            return 0;
        }

        get_last_node()->m_next = new_node;

        m_size++;

        return 0;
    }
    
    /* O(N) */
    template<typename T>
    inline T singly_linked_list<T>::top_back()
    {
        if (empty())
        {
            std::cout << " list is empty " << std::endl;
            return T();
        }

        return get_last_node()->m_data;
    }
    
    /* O(N) */
    template<typename T>
    inline T singly_linked_list<T>::pop_back()
    {
        T value;

        if (empty())
        {
            std::cout << " list is empty " << std::endl;
            return T();
        }

        if (size() == 1)
        {
            value = m_head->m_data;

            delete(m_head);

            m_head = nullptr;

            m_size = 0;

            return value;
        }


        node<T>* last_node = get_last_node();

        node<T>* last_node_before = get_node_until(size() -1);

        value = last_node->m_data;

        delete(last_node);

        last_node_before->m_next = nullptr;

        m_size--;

        return value;
    }
    
    /* O(N) - Worst case  */
    template<typename T>
    inline bool singly_linked_list<T>::find(T t_value)
    {
        return find_node(t_value) != nullptr;
    }
    
    /* O(N) - Worst case  */
    template<typename T>
    inline int singly_linked_list<T>::erase(T t_value)
    {
        /* Node with t_value deos not exists */
        if (empty())
            return -1;

        if (size() == 1)
        {
            delete(m_head);

            m_head = nullptr;

            m_size = 0;

            return 0;
        }

        int index = find_node_index(t_value);

        if (index == -1)
            return index;

        node<T>* node_to_erase = get_node_until(index + 1);
        node<T>* before_node_to_erase = get_node_until(index);
        node<T>* after_node_to_erase = get_node_until(index + 2);

        before_node_to_erase->m_next = after_node_to_erase;

        delete(node_to_erase);

        m_size--;

        return 0;
    }

    /* O(N) - Worst case  */
    template<typename T>
    inline void singly_linked_list<T>::erase_all()
    {
        while(m_head != nullptr)
        {
            node<T>* next_node = m_head->m_next;

            delete(m_head);

            m_size--;

            m_head = next_node;

        }
    }
    
    /* O(1) */
    template<typename T>
    inline bool singly_linked_list<T>::empty()
    {
        return (m_head == nullptr);
    }

    /* O(1) */
    template<typename T>
    inline size_t singly_linked_list<T>::size()
    {
        return m_size;
    }
    
    template<typename T>
    inline node<T>* singly_linked_list<T>::get_last_node()
    {
        node<T>* start_node = m_head;
        node<T>* last_node = nullptr;

        /* Traverse until the end */
        while (start_node != nullptr)
        {
            last_node = start_node;

            start_node = start_node->m_next;
        }

        return last_node;
    }

    template<typename T>
    inline node<T>* singly_linked_list<T>::get_node_until(size_t t_index)
    {
        node<T>* start_node = m_head;
        node<T>* until_node = nullptr;

        /* Traverse until the a node before last node */
        size_t index(1);
        while (start_node != nullptr)
        {
            until_node = start_node;

            if (index == t_index)
            {
                return until_node;
            }

            start_node = start_node->m_next;

            index++;
        }

        return nullptr;
    }

    template<typename T>
    inline node<T>* singly_linked_list<T>::find_node(T t_value)
    {
        node<T>* start_node = m_head;

        /* Traverse until the end */
        while (start_node != nullptr)
        {
            if (t_value == start_node->m_data)
                return start_node;

            start_node = start_node->m_next;
        }

        return nullptr;
    }

    template<typename T>
    inline int singly_linked_list<T>::find_node_index(T t_value)
    {
        node<T>* start_node = m_head;

        /* Traverse until the end */
        int index(0);
        while (start_node != nullptr)
        {
            if (t_value == start_node->m_data)
                return index;

            start_node = start_node->m_next;

            index++;
        }
        /* t_value not found*/
        return -1;
    }


    /* Returns the address of the specified node position 
    form the linke list chain */
    template<typename T>
    node<T>* singly_linked_list<T>::get_node_pointer(size_t t_position)
    {
        auto start_node{m_head};

        /* Traverse until the end */
        size_t i(0);
        while (start_node != nullptr)
        {
            if(i == t_position)
                return start_node;

            start_node = start_node->m_next;

            i++;
        }

        return nullptr;
    }
    
    template<typename U>
    node<U>* get_head(singly_linked_list<U>& t_list)
    {
        return t_list.m_head;
    }

}


答案 3 :(得分:0)

STL source将是一段要研究的代码。

您也可以尝试https://github.com/simonask/ftl/blob/master/list.hpp

两者都使用templates,应理解为能够制作任何通用类。

答案 4 :(得分:0)

struct1和struct2的字节大小不同,因此sizeof(struct1)!= sizeof(struct2)。从函数返回结构需要复制它,因此c ++要求您为其指定正确的类型,以便可以复制正确的字节数。要开始纠正这个问题,你需要在非常低的层次上思考:

struct GenericStruct {
   void *ptr;
   size_t size;
   type_info t;
};
struct1 extract_struct1(GenericStruct &s);
struct2 extract_struct2(GenericStruct &s)
  {
  if (s.size != sizeof(struct2)) throw -1;
  if (s.t != typeid(struct2)) throw -1;
  struct2 *s2 = (struct2*)s.ptr;
  return *s2;
  }
GenericStruct make_generic(const struct1 &ss)
 {
 GenericStruct s;
 s.ptr = (void*)&ss;
 s.size = sizeof(struct1);
 s.t = typeid(struct1);
 return s;
 }
 GenericStruct make_generic(const struct2 &ss);

真正的问题是,如果大小或类型不匹配,这些函数可能会在运行时失败。显然还需要复制:

GenericStruct Copy(const GenericStruct &s);

在存在这些基本原语之后,您可以创建一个具有复制构造函数和赋值运算符的类,该运算符使用这些函数来实现正确的通用结构支持。