2010-03-03 28 views
5

我开始学习C++,并作为一个练习决定实现一个简单的LinkedList类(下面有部分代码)。我有一个关于复制构造函数应该如何实现的问题,以及应该访问原始文件LinkedList上的最佳方式。LinkedList拷贝构造函数的实现细节

template <typename T> 
    class LinkedList { 

     struct Node { 
      T data; 
      Node *next; 

      Node(T t, Node *n) : data(t), next(n) {}; 
     }; 

    public: 
     LinkedList(); 
     LinkedList(const LinkedList&); 
     ~LinkedList(); 

     //member functions 
     int size() const;    //done 
     bool empty() const;   //done 
     void append(const T&);   //done 
     void prepend(const T&);  //done 
     void insert(const T&, int i); 
     bool contains(const T&) const; //done 
     bool removeOne(const T&);  //done 
     int removeAll(const T&);  //done 
     void clear();     //done 
     T& last();      //done 
     const T& last() const;   //done 
     T& first();     //done 
     const T& first() const;  //done 
     void removeFirst();   //done 
     T takeFirst();     //done 
     void removeLast(); 
     T takeLast(); 


     //delete when finished 
     void print();     
     //end delete 

     //operators 
     bool operator ==(const LinkedList<T> &other) const; //done 
     bool operator !=(const LinkedList<T> &other) const; //done 
     LinkedList<T>& operator =(const LinkedList<T> &other); //done 


    private: 
     Node* m_head; 
     Node* m_tail; 
     int m_size; 

    }; 

    template<typename T> 
    LinkedList<T>::LinkedList() : m_head(0), m_tail(0), m_size(0) { 

    } 
... 

使我的拷贝构造函数访问原始LinkedList直接的每个节点上的数据?

template<typename T> 
LinkedList<T>::LinkedList(const LinkedList& l) { 

    m_head = 0; 
    m_tail = 0; 
    m_size = 0; 

    Node *n = l.m_head; 

    // construct list from given list 
    while(n) { 
     append(n->data); 
     n = n->next; 
    } 
} 

或者我应该通过相应的存取访问数据? (我知道我没有定义访问者)。

此外,我打算创建一个自定义迭代器,以便可以遍历LinkedList。我应该使用复制构造函数来访问每个节点上的数据吗?

另一个问题(完全题外话,我知道),当和/或我们为什么要声明一个指向LinkedList

LinkedList<int> *l = new LinkedList<int>(); 

,而不是

LinkedList<int> l; 

回答

3

我以为会妥善追加处理最初的头部/尾部细节,是吗?如果是这样,你现在拥有的是伟大而简单的:浏览另一个列表,然后把它的项目添加到我的列表中。完善。

好吧,差不多。使用一个初始化列表来初始化成员变量:

template<typename T> 
LinkedList<T>::LinkedList(const LinkedList& l) : 
m_head(0), m_tail(0), m_size(0) 
{ 
// ... 
} 

而且,也许一个风格问题,这个炒菜锅,而不是while循环:

// construct list from given list 
for (Node *n = l.m_head; n != 0; n = n->next) 
    append(m->data); 

其实,我建议这个代替。当你有迭代器的时候,你会这样做:

for (const_iterator iter = l.begin(); iter != l.end(); ++iter) 
    append(*iter); 

它只是更好地遵循for循环的样式。 (初始化一些东西,检查一下,做点什么)。尽管对于迭代器来说,它可能会有所不同。 (更多更高版本)


或者我应该通过相应的存取访问数据? (我知道我没有定义访问者)。

此外,我打算创建一个自定义迭代器,以便可以迭代LinkedList。我应该使用复制构造函数来访问每个节点上的数据吗?

这些迭代器是您的访问器。你不要想暴露你的内部头尾指针,这是一个灾难的秘诀。该课的目的是为了而不是揭露细节。也就是说,迭代器是围绕这些细节的抽象包装。

一旦你有你的迭代器,那么你可以使用它们遍历列表而不是指针算术。这与最近的asked question有关。一般来说,你应该使用抽象来处理你的数据。所以是的,一旦你有你的迭代器 ,你应该使用它们遍历数据。

大多数提供迭代器的类也提供了一种在开始和结束迭代器时插入数据的方法。这通常被称为insert,如下所示:insert(iterBegin, iterEnd)。这循环遍历迭代器,将其数据附加到列表中。

如果你有这样的功能,你的拷贝构造函数仅是:

insert(l.begin(), l.end()); // insert the other list's entire range 

insert就像for循环我们上面已经实施。


另一个问题(完全题外话,我知道),当和/或我们为什么要声明一个指针链表

链表* L =新的LinkedList();而不是LinkedList l;

第一个是动态分配,第二个是自动(堆栈)分配。你应该更喜欢堆栈分配。它几乎总是更快,更安全(因为你不需要删除任何东西)。实际上,一个名为RAII的概念依赖于自动存储,因此保证析构函数可以运行。

只有在必要时才使用动态分配。

+1

非常感谢! :) – bruno 2010-03-03 20:16:29

0

我认为这是一个非常有价值的练习来实现自己的链表,因为它可以帮助您了解指针,数据结构等的细节。只要确保不要在实际代码中使用链表类许多已经编写和测试过的现有库。最好的代码是你不必写的代码。请参阅std::list

我看到你实现你的拷贝构造函数的方式没有问题。你可以把代码移动到一个专用的复制函数中,然后从构造函数中调用它,这样你就不需要维护更多的代码。但是通常从类内部使用类的实现细节不仅是可以接受的,而且在许多情况下也是可取的,因为它会尽可能快。

至于创建列表与新的vs创建它在堆栈上,这是一个决定,不仅适用于你的类,但一般的数据结构。过度简化的经验法则:如果可以,在堆栈上分配,如果需要,可以在堆上分配。例如,如果您需要列表以超过创建它的功能,则需要使用该列表。

如果你决定使用new来分配堆,你应该使用智能指针,而不是试图自己管理内存。现在就让自己养成这种习惯。不要等到你正在编写'真正'的代码。你遇到的许多人在你的搜索中将无助于编写更好的代码,并会坚持“只使用new”。