【问题标题】:Generic linked list in c++C++中的通用链表
【发布时间】:2011-06-28 15:03:53
【问题描述】:

我一直在为一个关于如何在 C++ 中创建通用链表的相当简单的问题而苦苦挣扎。该列表应该能够包含多种类型的结构,但每个列表将仅包含一种类型的结构。当我想实现 getNode() 函数 [见下文] 时,问题就出现了,因为我必须指定它应该返回哪个结构。我试图用类替换结构,其中 getNode 函数返回一个由所有其他类继承的基类,但它仍然不能解决问题,因为编译器不允许 getNode 函数返回除然后是基类。

所以这里有一些代码sn-p:

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。 感谢提示或帮助

【问题讨论】:

标签: c++ struct linked-list


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢!显然 std::list 选项很快解决了我的问题,但我会看看模板以满足未来的需求
【解决方案2】:

基本模板很简单。

只需将类声明为带有模板类型变量的模板即可。
现在,无论您在哪里都有想要泛型的声明类型,在类中,将显式类型名称替换为模板化变量名称。

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

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

}; 

【讨论】:

  • 感谢 yochai,我不能奖励积分,但我很欣赏教学示例
【解决方案3】:

STL source 是一段值得研究的代码。

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

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

【讨论】:

  • ...认真的吗?你会建议那些似乎不知道模板的人查看 STL 源代码吗?
  • 是的,需要学习模板,但将模板指向正确的方向将对编码人员有利。感谢您的评论。
【解决方案4】:

这是一个不使用 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;
    }

}


【讨论】:

    【解决方案5】:

    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);
    

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

    【讨论】:

      猜你喜欢
      • 2015-05-05
      • 2012-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多