【问题标题】:Singly linked list assignment operator overload in C++C++ 中的单链表赋值运算符重载
【发布时间】:2016-02-24 19:57:04
【问题描述】:

我有以下单链表实现。

template <typename T> struct node{
    node(T data):data(data),next(nullptr){}
    T data;
    node<T> * next;
};

template< typename T> class slist{
    node<T>* head;
    int size;
public:
    slist(node<T>* head):head(head), size(0){}

    slist(const slist<T>& rhs){

        node<T>* temp = rhs.getHead();
        node<T>* p = new node<T>(temp->data);
        head = p;
        node<T>* current = p;
        while(temp != nullptr){
            current = current->next;
            current->data = temp->data;
        }

    }
    ~slist(){
        if(head == nullptr) return;

        while(head != nullptr){
            node<T>* current = head;
            head = head->next;
            delete(current);
        }
    }
    slist& operator= (const slist& rhs){


    }
    node<T>* getHead()const {
        return head;
    }


    void insertFront(T item){
        node<T>* p = new node<T>(item);
        if(head == nullptr){
            p = head;
            size++;
            return;
        }
        p->next = head;
        head = p;
        size++;
    }

    void insertBack(T item){
        node<T>* p = new node<T>(item);
        node<T>* current = head;
        while(current->next != nullptr){
            current = current->next;
        }
        current->next = p;
        size++;
    }

    void remove(T item){
        bool check = false;

        node<T>* current = head;
        try {
            while(current != nullptr){
                if(current->data == item) check = true;
                current = current->next;
            }
            if(!check){
                throw std::runtime_error("Item not in list");

            }
        }catch(std::runtime_error& e){
            std::cout<<e.what()<<std::endl;
            exit(-1);
        }

        current = head;
        while(current != nullptr){
           if(current->next->data == item){
               node<T>* temp = current->next;
               current->next = current->next->next;
               delete(temp);
               break;
           }
            current = current->next;
        }
        size--;

    }
    int getSize () const {
        return size;
    }

    void printList(){
        node<T>* current = head;
        while(current != nullptr){
            if(current->next != nullptr){
                std::cout<<current->data<<"->";
            }else{
                std::cout<<current->data<<std::endl;
            }
            current = current->next;
        }
    }


};

基于类和复制构造函数的当前实现,有人可以帮助赋值运算符重载。此外,我对复制构造函数和赋值重载有点困惑。我的理解是,复制构造函数会创建一个新列表,该列表与旧列表中的旧列表具有相同的值。所有节点的下一个地址将不同,但值将相同,因为它是深层副本。我的理解是否正确,然后赋值重载有什么作用?

【问题讨论】:

  • 复制分配与复制构造具有相同的目标。您只需要知道容器中已经有数据。复制构造函数应该创建一个与原始容器具有相同数据的容器,但它是一个独立的对象。
  • 使用复制和交换习语(google it)。
  • 在您的复制构造函数中,您需要使用new 创建每个节点,而不仅仅是第一个。 (循环内应该有一个new。)

标签: c++ singly-linked-list


【解决方案1】:

你需要考虑这些情况:

  • 复制构造,即slist&lt;int&gt; newone = other;
  • 复制分配,即slist&lt;int&gt; a; /* ... */ a = other;

现在,顾名思义,这两个操作都会生成原始数据结构的副本。这究竟意味着什么取决于您希望如何实现它,但以下应该 - 保持接近最不意外的原则 - 保持:

slist<int> a = some_generator(), b = another_generator();
slist<int> c = a;
// a == c should be true
b = a;
// a == b should be true now
modify(a);
// a == b should be false now, BUT b should be in the same state as before!

实现这一点的最简单方法是制作 - 正如您已经建议的那样 - 深拷贝。因此,您基本上执行与复制构造函数相同的操作。您制作每个节点的副本,以使它们与原始节点具有相同的值,但它们是不同的实体。

如果您还以“现代 C++”(即 C++11 及更高版本)为目标,那么您还有一个 move 构造函数 和一个 move 赋值运算符可能想要实现。


如 cmets 中所述,您的深拷贝算法不正确:您需要为每个节点制作一份副本:

// if in assigment, delete the nodes pointed to by head first!
node<T> const * iterator = other.getHead();
if (iterator != nullptr) { // in your implementation, head could be a nullptr
  node<T> * new_node = new node<T>(*iterator); // make a copy of head
  head = new_node;
  while (iterator->next) {
    iterator = iterator->next;
    node<T> * copy = new node<T>(*iterator);
    new_node->next = copy;
    new_node = copy;
  }
  new_node->next = nullptr;
}

另外,如果可以的话,更喜欢智能指针,在这种情况下是unique_ptr,而不是原始指针。

【讨论】:

    【解决方案2】:

    如果你已经有了拷贝构造函数和析构函数,也实现swap(),然后根据这三个实现你的拷贝赋值运算符,例如:

    template <typename T>
    slist<T>& slist<T>::operator= (slist<T> other) {
        this->swap(other);
        return *this;
    }
    

    请注意,参数很容易被复制:与复制构造函数不同,复制赋值可以按值获取其参数。

    关于语义:

    • 复制构造函数创建一个与原始值相同的新对象,例如:

      slist<int> sl1;
      // insert elements into sl1
      slist<int> sl2(sl1); // uses the copy constructor
      
    • 复制赋值将现有对象的值替换为已分配值的值,例如

      slist<int> sl1;
      slist<int> sl2;
      // possibly modify both sl1 and sl2
      sl2 = sl1; // uses the copy assignment operator
      

    【讨论】:

      猜你喜欢
      • 2019-04-02
      • 2015-04-07
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2013-03-30
      • 2016-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多