2010-03-03 69 views
5

Tôi bắt đầu học C++ và bài tập quyết định triển khai lớp đơn giản LinkedList (Bên dưới là một phần của mã). Tôi có một câu hỏi liên quan đến cách xây dựng bản sao nên được thực hiện và cách tốt nhất các dữ liệu trên bản gốc LinkedList nên được truy cập.Chi tiết triển khai bản sao của LinkedList sao chép

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

    } 
... 

Nhà xây dựng bản sao của tôi có thể truy cập dữ liệu trên mỗi nút của bản gốc LinkedList trực tiếp không?

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

Hoặc tôi có nên truy cập dữ liệu thông qua người truy cập tương ứng không? (Tôi biết rằng tôi không có (các) người truy cập được xác định).

Ngoài ra, tôi dự định tạo trình lặp vòng tùy chỉnh để có thể lặp lại qua số LinkedList. Tôi có nên sử dụng trong constructor sao chép để truy cập dữ liệu trên mỗi nút?

Một câu hỏi (hoàn toàn off-topic, tôi biết), khi nào và/hoặc lý do tại sao chúng ta nên khai báo một con trỏ đến một LinkedList

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

thay vì

LinkedList<int> l; 

Trả lời

3

tôi giả sử thêm sẽ đúng cách xử lý chi tiết đầu/đuôi ban đầu, vâng? Nếu vậy, những gì bạn có bây giờ là tuyệt vời và đơn giản: Đi qua danh sách khác, và lấy mục của nó và thêm một bản sao vào danh sách của tôi. Hoàn hảo.

Vâng, gần như vậy. Sử dụng một danh sách initializer để khởi tạo các biến thành viên:

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

Ngoài ra, có thể là một vấn đề của phong cách, đây woks thay vì một vòng lặp while:

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

Trong thực tế, tôi khuyên bạn nên thay thế. Khi bạn có các trình vòng lặp, bạn sẽ làm một cái gì đó như:

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

Nó chỉ theo phong cách của vòng lặp tốt hơn. (Khởi tạo một cái gì đó, kiểm tra một cái gì đó, làm điều gì đó). Mặc dù cho vòng lặp, nó có thể sẽ khác nhau. (Xem thêm sau)


Hoặc tôi nên truy cập dữ liệu thông qua các accessor tương ứng? (Tôi biết rằng tôi không có (các) người truy cập được xác định).

Ngoài ra, tôi dự định tạo một trình vòng lặp tùy chỉnh để có thể lặp qua Danh sách được Liên kết. Tôi có nên sử dụng trong constructor sao chép để truy cập dữ liệu trên mỗi nút?

Những người lặp đó là người truy cập của bạn. Bạn không muốn hiển thị con trỏ đầu đuôi bên trong của bạn, đó là công thức cho thảm họa. Mục đích của lớp học là không phải là hiển thị chi tiết. Điều đó nói rằng, vòng lặp là trình bao bọc trừu tượng xung quanh những chi tiết đó.

Khi bạn có trình vòng lặp của mình, khi đó bạn có thể sử dụng chúng để lặp qua danh sách thay vì số học con trỏ. Điều này liên quan đến điều này gần đây asked question.Nói chung, bạn nên sử dụng abstractions của bạn để đối phó với dữ liệu của bạn. Vì vậy, có một khi bạn có iterators của bạn tại chỗ, bạn nên sử dụng những người để iterate trên dữ liệu.

Hầu hết các lớp cung cấp trình vòng lặp cũng cung cấp cách chèn dữ liệu cho trình lặp đầu và kết thúc. Điều này thường được gọi là insert, như sau: insert(iterBegin, iterEnd). Vòng lặp này thông qua các trình vòng lặp, thêm dữ liệu của nó vào danh sách.

Nếu bạn có chức năng như vậy, sao chép-constructor của bạn sẽ chỉ đơn giản là:

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

đâu insert được thực hiện như đối với vòng lặp chúng tôi đã có ở trên.


Một câu hỏi (hoàn toàn off-topic, tôi biết), khi nào và/hoặc lý do tại sao chúng ta nên khai báo một con trỏ đến một LinkedList

LinkedList * l = new LinkedList(); thay vì LinkedList l;

Đầu tiên là phân bổ động, thứ hai là phân bổ tự động (ngăn xếp). Bạn nên chọn phân bổ stack. Nó hầu như luôn luôn nhanh hơn và an toàn hơn (vì bạn không cần xóa bất kỳ thứ gì). Trong thực tế, một khái niệm gọi là RAII dựa vào bộ nhớ tự động, do đó, các trình phá hủy được đảm bảo để chạy.

Chỉ sử dụng phân bổ động khi bạn có.

+1

Cảm ơn bạn rất nhiều !! :) – bruno

0

Tôi nghĩ rằng nó vẫn là một bài tập rất có giá trị để thực hiện danh sách liên kết của riêng bạn vì nó giúp bạn tìm hiểu chi tiết về con trỏ, cấu trúc dữ liệu, v.v. Hãy chắc chắn không sử dụng lớp danh sách được liên kết của bạn bằng mã thực là nhiều thư viện hiện có đã được writtern và thử nghiệm. Mã tốt nhất là mã bạn không phải viết. Xem std::list.

Tôi thấy không có vấn đề gì theo cách bạn đã triển khai trình tạo bản sao của mình. Bạn có thể làm tốt để di chuyển mã đến một hàm sao chép chuyên dụng và gọi nó từ hàm tạo, tuy nhiên, để có ít mã bạn phải duy trì. Nhưng nói chung bằng cách sử dụng các chi tiết thực hiện của lớp học của bạn từ bên trong lớp chính nó là không chỉ chấp nhận được, nhưng trong nhiều trường hợp ưa thích vì nó sẽ càng nhanh càng tốt.

Để tạo danh sách mới và tạo nó trên ngăn xếp, đây là một quyết định không chỉ áp dụng cho lớp học của bạn mà là cấu trúc dữ liệu nói chung. Quy tắc ngón tay cái quá đơn giản: phân bổ trên ngăn xếp nếu bạn có thể, phân bổ trên heap nếu bạn cần. Bạn sẽ cần phải ví dụ nếu bạn cần danh sách để outlive chức năng mà nó được tạo ra.

Và một lần nữa quay trở lại để không tự tay cán mã của bạn, nếu bạn quyết định sử dụng new để phân bổ trên heap, bạn nên sử dụng con trỏ thông minh thay vì cố gắng tự quản lý bộ nhớ. Hãy tự tìm hiểu thói quen này ngay bây giờ. Đừng chờ đợi cho đến khi bạn đang làm việc trên mã 'thực'. Nhiều người bạn gặp sẽ vô ích với bạn trong tìm kiếm của bạn để viết mã tốt hơn, và sẽ nhấn mạnh vào "chỉ sử dụng new".

Các vấn đề liên quan