簡體   English   中英

在單個鏈表C ++中實現復制構造函數

[英]Implementing copy constructor in a single linked list C++

我有一個C ++代碼:

#include <iostream>
using namespace std;
struct Node;

typedef Node *NodePtr;

struct Node
{
    int Item;
    NodePtr Next;
};

class LinkedList
{
public:
    LinkedList();                   // default constructor
    ~LinkedList();                  // destructor
    void AddTail(int);              // adds item to tail
private:
    NodePtr Head;
};

LinkedList::LinkedList()
{
    Head = NULL; //declare head as null
}
//Adding on tail
void LinkedList::AddTail(int Item)
{
    NodePtr Crnt;
    NodePtr node = new Node;
    node->Item = Item;
    node->Next = NULL;
//if head is in null declare the added node as head
    if (Head == NULL)
    {
        Head = node;
    }
    else
    { //set the current to head, move the current node to current next node
        Crnt = Head;
        while (Crnt->Next != NULL)
        {
            Crnt = Crnt->Next;
        }
        //Add item to the tail of the linked list
        Crnt->Next = node;
    }
}

int main()
{
    LinkedList la;
    la.AddTail(30);
    la.AddTail(60);
    la.AddTail(90);
    LinkedList lb;
    return 0;
}

所以我的問題是如何實現一個復制構造函數(假設在對象lb上),它創建了list參數的深層副本,還添加了用於在空和非空列表上測試復制構造函數的代碼? 提前致謝。

編程的一個重要規則是不要重復自己(DRY)。 如果您有一個添加的功能,並且您知道它可以正常工作,請繼續將其用於與添加相關的作業。 這意味着保持添加死愚蠢和多才多藝符合您的最佳利益。

應用DRY原理,復制構造函數(假設AddTail方法正常工作)非常簡單:為源列表中的每個節點調用AddTail

LinkedList::LinkedList(const LinkedList & src):Head(nullptr)
{
    NodePtr node = src.Head;
    while (node != nullptr)
    {
        AddTail(node->Item);
        node = node->Next;
    }
}

由於Copy和Swap Idiom ,擁有一個工作副本構造函數使得賦值運算符也很簡單:

LinkedList & LinkedList::operator=(LinkedList src) 
// pass by reference performs the copy
{
    std::swap(Head, src.Head); // now just swap the head of the copy 
                               // for the head of the source
    return *this;
} // destructor fires for src and cleans up all the nodes that were on this list 

為了完成三個法則,我們需要一個析構函數。 這與復制構造函數一樣是DRY的應用程序:反復調用節點刪除功能,直到列表為空。 刪除函數幾乎是任何鏈表的必然要求,所以在這里我假設有一個名為Remove

LinkedList::~LinkedList()
{
    while (Head != nullptr)
    {
        NodePtr temp = Head;
        Remove(Head);
        delete temp;
    }
}

因此,現在基於鏈接列表無法運行的兩個功能,我們已經實現了基本維護所需的所有其他功能。 您所需要的只是經過測試和無錯誤的AddRemove功能,其余的幾乎是免費的。

而且因為AddTail函數擊中了我的一個寵物洞......這是一個大大降低函數復雜性的技巧:

void LinkedList::AddTail(int Item)
{
    NodePtr *Crnt = &Head; // instead of pointing where Head points, point at 
                           // Head now we don't care if it is head or any 
                           // other node's Next. They are all abstracted to 
                           // the same thing: A pointer to where the next 
                           // node can be found        
    while (*Crnt != NULL) // keep looking until end of list
    {
        Crnt = &(*Crnt)->Next; // by pointing at the next Next pointer
    }
    //Add item to the tail of the linked list
    NodePtr node = new Node;
    node->Item = Item;
    node->Next = NULL;
    *Crnt = node; // Now just plop the new node over top of the terminating NULL
}

我沒有實現的Remove函數使用相同的指針 - 指針技巧。

試試這個( https://ideone.com/9lywXc使用您原來發布的代碼)

LinkedList::LinkedList(const LinkedList& other):Head(nullptr)
{
    cout << "copy constructor called:\n";
    if(other.Head == nullptr) return;
    NodePtr dummyHead = new Node;
    NodePtr curr = dummyHead;
    NodePtr othcurr = other.Head;
    for(; othcurr!=nullptr; othcurr = othcurr->Next)
    {
        curr->Next = new Node;
        curr = curr->Next;
        cout << (curr->Item = othcurr->Item) << ",";
        curr->Next = nullptr;
    }
    Head = dummyHead->Next;
    delete dummyHead;
}


int main()
{
    LinkedList la;
    la.AddTail(30);
    la.AddTail(60);
    la.AddTail(90);
    LinkedList lb(la);
    return 0;
}

輸出:

復制構造函數調用:

30,60,90,

這是對user4581301之前的答案的改進,因此復制構造函數在輸入列表大小上是O(n)。 在封裝列表類中使用一個額外的指針跟蹤尾部:

class LinkedList
{
public:
    LinkedList():Head(nullptr),Tail(nullptr){}
    LinkedList(const LinkedList& other);
    ~LinkedList() = default;                  // destructor
    void AddTail(int);              // adds item to tail
private:
    NodePtr Head;

    //KEEP TRACK OF TAIL POINTER WITH EXTRA MEMBER
    NodePtr Tail;
};

//via user4581301 response
LinkedList::LinkedList(const LinkedList & src):Head(nullptr),Tail(nullptr)
{
    NodePtr node = src.Head;
    while (node != nullptr)
    {
        AddTail(node->Item);
        node = node->Next;
    }
}

//Adding on tail
void LinkedList::AddTail(int Item)
{
    NodePtr np = new Node{Item,nullptr};
    if(Tail == nullptr && Head == nullptr)
        Head = Tail = np;
    else
    {
        Tail->Next = np;
        Tail = Tail->Next;
    }
}

關於測試:您可以添加功能來吐出列表的內容。 或者您可以將其子類化為測試擴展。 或者你可以使用operator <<()破壞這樣的封裝:

class LinkedList
{
public:
    LinkedList():Head(nullptr),Tail(nullptr){}
    LinkedList(const LinkedList& other);
    ~LinkedList() = default;                  // destructor
    void AddTail(int);              // adds item to tail

    //breaks encapsulation but make some nice sugar to look inside
    friend ostream& operator<<(ostream& s, LinkedList& l)
    {
        s << "list contents: ";
        NodePtr c = l.Head;
        for(; c!=nullptr;c=c->Next)
            s << c->Item << " ";
        s << endl;
        return s;
    }
private:
    NodePtr Head;
    NodePtr Tail;
};

以便

int main()
{
    LinkedList la;
    la.AddTail(30);
    la.AddTail(60);
    la.AddTail(90);
    LinkedList lb(la);
    cout << lb;
    return 0;
}

吐出:列表內容:30 60 90

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM