1. ホーム
  2. c++

[解決済み] 単一リンクリストでのコピーコンストラクタの実装 C++

2022-02-19 06:10:40

質問

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を想定)をどのように実装し、また、空と空でないリストでコピーコンストラクタをテストするコードを追加すれば良いでしょうか? 事前にありがとうございます。

どのように解決するのですか?

プログラミングの大きなルールのひとつにDRY(Don't Repeat Yourself)があります。もし、足し算をする関数があって、それがうまくいくことが分かっているなら、足し算関連の仕事のためにそれを使い続けましょう。つまり、addの死語と汎用性を保つことが得策なのです。

DRYの考え方を応用して、コピーコンストラクタを想定した AddTail メソッドが正しく機能するようになり、驚くほどシンプルになりました。を呼び出すだけです。 AddTail をソースリスト内のすべてのノードに適用します。

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

また、コピーコンストラクタが動作していることで、代入演算子は コピーとスワップのイディオム これも笑えるほど簡単です。

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 

仕上げに 3つのルール 三拍子揃ったデストラクタが必要です。これは コピーコンストラクタはDRYの応用で、リストが空になるまで何度もノードの削除関数を呼び出すことができます。削除関数はどんなリンクリストでもほぼ確実に必要なものなので、ここでは Remove .

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

このように、Linked Listはとにかくこの2つの関数がないと機能しないので、これをベースに、基本的なメンテナンスに必要な他の関数をすべて実装しました。必要なのは、テスト済みでバグのない 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 関数も同じようにポインタ-to-ポインタのトリックを使っています。