1. ホーム
  2. c++

[解決済み] リンクリストにおける代入演算子 C++

2022-02-17 06:49:04

質問

c++でリンクリストを実装しようとしています。

私はこのように代入演算子を実装しています。

// assignment operator
template<class T>
LinkedList<T>& LinkedList<T>::operator = (const LinkedList& rhs) {

if (&rhs != this) {
    Node *tmp = head;

    while (tmp -> next) {
        head = head -> next;
        delete tmp;
        tmp = head;
    }

    tmp = rhs -> head;

    while (tmp) {
        append(tmp);
        tmp = tmp -> next;
    }
}

    return *this;
}

メイン関数では、以下のコードでテストしています。

LinkedList<int> *lst1 = new LinkedList<int>(7);

LinkedList<int> *lst2 = new LinkedList<int>(101);

std::cout << lst1 -> head -> data << std::endl;
std::cout << lst2 -> head -> data << std::endl;

lst1 = lst2;

std::cout << lst1 -> head -> data << std::endl;

delete lst1;
delete lst2;            <--------------- error here

期待通り、コンソールに出力されました。

7 101 101

しかし、プログラムがlst2を削除しようとすると、次のようなエラーが表示されます。

解放されるポインタが割り当てられていない

デバッガーを使って、プログラムがいつ代入を行っているかを調べています。

lst1 = lst2;

lst1はlst2のコピーを取得するのではなく、実際にはlst2を指すアドレスを参照しているので、lst1を削除すると、lst2はすでになくなっています。

では、どなたか私の代入演算子のどこがおかしいのか、教えていただけませんか?

初心者の質問で申し訳ないのですが、数時間かけてもわからなくなりました。

私が完成させたコードは以下の通りです。

template<class T>
class LinkedList {

private:
    class Node {
    public:
        T data;
        Node *next;

        // default constructor
        Node() = default;

        // constructor with data
        Node(const T& data) : data(data), next(NULL) {}

    };

public:
    Node *head;

    LinkedList(const LinkedList& copyLst);
    LinkedList& operator=(const LinkedList& byValList);
    LinkedList() : head(NULL){}
    LinkedList(Node *newNode) : head(newNode) {}
    LinkedList(T val) {
        head = new Node(val);
    }
    ~LinkedList();

    static LinkedList<int> sumLists(const LinkedList<int>& lst1, const LinkedList<int>& lst2) ;

    void insertAtFront(T val);
    void insertAtEnd(T val);
    void printList();
    void insert(T val);

    void append(const Node&);
};

// copy constructor
template<class T>
LinkedList<T>::LinkedList(const LinkedList<T>& copyLst) {

    const Node *cpCurrent = copyLst.head;
    Node *lsCurrent = NULL;

    if (cpCurrent != NULL) {
        head = new Node(cpCurrent -> data);
        lsCurrent = head;

        cpCurrent = cpCurrent -> next;

    }

    while (cpCurrent != NULL) {
        Node *newNode = new Node(cpCurrent -> data);
        lsCurrent -> next = newNode;

        lsCurrent = lsCurrent -> next;
        cpCurrent = cpCurrent -> next;
    }
}

// assignment operator
template<class T>
LinkedList<T>& LinkedList<T>::operator = (const LinkedList& rhs) {

    if (&rhs != this) {
        Node *tmp = head;

        while (tmp -> next) {
            head = head -> next;
            delete tmp;
            tmp = head;
        }

        tmp = rhs -> head;

        while (tmp) {
            append(tmp);
            tmp = tmp -> next;
        }
    }

    return *this;
}

// destructor
template<class T>
LinkedList<T>::~LinkedList() {
    Node *current = head;

    while (current != NULL) {
        head = head -> next;
        delete current;
        current = head;
    }
}

template<typename T>
void LinkedList<T>::append(const Node& node ){

    if (NULL == head) {
        Node *newNode = new Node(node -> data);
        head = newNode;
    } else {
        Node *current = head;

        while (current -> next) {
            current = current -> next;
        }


        Node *newNode = new Node(node -> data);
        current -> next = newNode;
    }
}

解決方法は?

現在の実装は、コピーコンストラクタにすでに存在するコードと重複しているので、それを再利用してはどうでしょうか?

コピーコンストラクタとデストラクタが動作している場合、コピー/スワップイディオムを使用するのが、代入演算子を実装する最も簡単で安全な方法でしょう。

#include <algorithm>
//...
template<class T>
LinkedList<T>& LinkedList<T>::operator = (const LinkedList<T>& rhs) 
{
    LinkedList<T> temp(rhs);
    std::swap(temp.head, head);
    return *this;
}

コピーコンストラクタとデストラクタが正しく動作することを前提とすれば、これは正しく動作することが保証されています。 コピーを作成します。 temp オブジェクトの rhs の中身と入れ替える。 *this . このとき temp の中にあった古いデータも一緒に破棄されます。 *this .

C++11コンパイラをお持ちの方は、pass-by-valueで渡されたパラメータに対するmoveの構築を利用することができます。

#include <algorithm>
//...
template<class T>
LinkedList<T>& LinkedList<T>::operator = (LinkedList<T> rhs) 
{
    std::swap(rhs.head, head);
    return *this;
}

* コピー&スワップ・イディオムを使用する場合は、クラスのすべてのメンバーを交換する必要があることに留意してください。