2017-09-14 86 views
0

我不知道我的移动赋值运算符有什么问题,这里是函数。我不认为我正确抓取数据,因为当我运行测试,我得到一个随机负数和“你的程序已停止工作)移动赋值运算符C++

virtual LinkedList<T> &operator=(LinkedList<T> &&other) 
{ 
    cout << " [x] Move *assignment* operator called. " << endl; 

    // Delete our own elements 
    ListNode<T> *temp1 = _front; 
    while (temp1 != nullptr) 
    { 
     ListNode<T> *n = temp1->getNext(); 
     delete temp1;   
     temp1 = n; 
    } 
    // Grab other data for ourselves 
    ListNode<T> *temp2 = other._front; 
    while (temp2 != nullptr) 
    { 
     addElement(temp2->getValue()); 
     temp2 = temp2->getNext(); 
    } 
    // Reset their pointers to nullptr 

    other._front = nullptr; 
    other._end = nullptr; 
    other._size = 0; 
    other._last_accessed_index = 0; 
    other._last_accessed_node = nullptr; 

    return *this; 
} 

测试代码 - 这是我的老师测试代码 -

// Use move *assignment* operator 
cout << " [x] Test #5: Move *assignment* constructor behavior" << endl; 
moved1 = LinkedList<int>{ 6, 7, 8, 9, 10 }; 
cout << " [x] Result:" << endl; 
cout << " [x] Expected:\t6 7 8 9 10" << endl; 
cout << " [x] Actual:\t\t"; 
for (int i = 0; i < moved1.getSize(); i++) 
{ 
    cout << moved1.getElementAt(i) << " "; 
} 
cout << endl << endl; 

这是我第一次与移动和移动赋值运算符的工作谢谢:)

+0

当您逐步完成代码时,调试器会显示什么内容? –

+0

它会运行所有的东西,但是当我运行我的测试代码时,它试图接收列表中的数据时会中断@KenWhite –

+0

您能发布测试代码 - 或者最好是它的最小子集,它可以演示问题吗? – norlesh

回答

1

这不是移动赋值运算符的正确实现。它看起来更像是一个拷贝赋值操作符(但不是很好,因为它会泄漏内存)。

一个典型的移动赋值运算符看起来更像这个:

#include <utility> 

LinkedList<T>& operator=(LinkedList<T> &&other) 
{ 
    cout << " [x] Move *assignment* operator called. " << endl; 

    std::swap(_front, other._front); 
    std::swap(_end, other._end); 
    std::swap(_size, other._size); 
    std::swap(_last_accessed_index, other._last_accessed_index); 
    std::swap(_last_accessed_node, other._last_accessed_node); 

    return *this; 
} 

一招赋值运算符不应该免费什么移动将源内容的所有权移交给目标对象,反之亦然。让源对象释放目标对象的以前的内容时,源对象赋值运算符退出后销毁,所以一定要确保类也有一个适当的析构函数的实现:

~LinkedList() 
{ 
    // Delete our elements 
    ListNode<T> *temp = _front; 
    while (temp != nullptr) 
    { 
     ListNode<T> *n = temp->getNext(); 
     delete temp;   
     temp = n; 
    } 
} 

良好的措施,这里是什么副本构造函数,将构造函数和拷贝赋值运算符可能看起来像:

LinkedList() : 
    _front(nullptr), 
    _end(nullptr), 
    _size(0), 
    _last_accessed_index(0), 
    _last_accessed_node(nullptr) 
{ 
    cout << " [x] Default *constructor* called. " << endl; 
} 

LinkedList(const LinkedList<T> &src) 
    : LinkedList() 
{ 
    cout << " [x] Copy *constructor* called. " << endl; 

    ListNode<T> *temp = src._front; 
    while (temp != nullptr) 
    { 
     addElement(temp->getValue()); 
     temp = temp->getNext(); 
    } 
} 

LinkedList(LinkedList<T> &&src) 
    : LinkedList() 
{ 
    cout << " [x] Move *constructor* called. " << endl;  
    src.swap(*this); 
} 

LinkedList(initializer_list<T> src) 
    : LinkedList() 
{ 
    cout << " [x] Initialization *constructor* called. " << endl; 

    const T *temp = src.begin(); 
    while (temp != src.end()) 
    { 
     addElement(*temp); 
     ++temp; 
    } 
} 

LinkedList<T>& operator=(const LinkedList<T> &other) 
{ 
    cout << " [x] Copy *assignment* operator called. " << endl; 

    if (&other != this) 
     LinkedList<T>(other).swap(*this); 

    return *this; 
} 

LinkedList<T>& operator=(LinkedList<T> &&other) 
{ 
    cout << " [x] Move *assignment* operator called. " << endl; 
    other.swap(*this);   
    return *this; 
} 

void swap(LinkedList<T> &other) 
{ 
    std::swap(_front, other._front); 
    std::swap(_end, other._end); 
    std::swap(_size, other._size); 
    std::swap(_last_accessed_index, other._last_accessed_index); 
    std::swap(_last_accessed_node, other._last_accessed_node); 
} 

的复制和移动赋值运算符其实可以合并为一个单一的实现,由值取输入对象,并让编译器决定是否在初始化该对象时使用复制或移动语义,基于上下文中调用运营商:

LinkedList<T>& operator=(LinkedList<T> other) 
{ 
    cout << " [x] *assignment* operator called. " << endl; 
    swap(other); 
    return *this; 
} 
+0

非常感谢您添加的所有内容!对于这个练习,我认为“移动赋值运算符”只是为了模拟移动赋值运算符使用“复制”方法所做的操作,如果这是有意义的话。目前,当我需要10 9 8 7 6时,我的代码会输出1 2 3 4 5,如果您可以为此提供任何澄清,将不胜感激。 –

+0

再次感谢所有这些信息,但是我无法理解所有的概念:) –

+0

使用复制语义实现移动赋值操作符是错误的。这打破了移动语义学的所有好处。如果你打算这样做,你可以直接实现一个拷贝赋值操作符。我给你的代码不应该输出'1 2 3 4 5',如果它给了'6 7 8 9 10'。如果您在移动分配后获得'1 2 3 4 5',则表示您做错了事。 –

0

很难,而无需代码的其余部分是肯定的,但它看起来像你不正确地清除正在分配的列表

当你这样做:

// Delete our own elements 
ListNode<T> *temp1 = _front; 
while (temp1 != nullptr) 
{ 
    ListNode<T> *n = temp1->getNext(); 
    delete temp1;   
    temp1 = n; 
} 

你实际上并没有从this删除的元素。因此,moved1包含已删除的节点,并且在开始循环时执行失败。你想要做的就是在删除它们之前从列表中删除节点。

去会的方式:

// Remove our own elements 
ListNode<T> *temp1 = _front; 
while (temp1 != nullptr) 
{ 
    ListNode<T> *n = temp1->getNext(); 
    pop(); 
    delete temp1;   
    temp1 = n; 
} 

而且有这样的方法:

void pop() // removes the first element from the list 
{ 
    _front = _end._front; 
    _end = _end._end; 
    --_size; 
} 

。当然pop定义取决于你的全面落实之类的。如果你正在存储指向给你的对象的指针,你可能不应该删除它们。但是,如果您使用的是额外的包装,例如ListNode,则您的pop方法应删除它们 - 尽管在包装的情况下,最好不要使用指针。

你可以看看std::list::pop_front了解更多信息。

+0

感谢您的回应!当我按照你的建议运行代码时,列表中的数字为1 2 3 4 5 6 7 8 9 10,而我想输出10 9 8 7 6,但现在我的输出是1 2 3 4 5 –

+0

它因为removeElementAt已经删除它?在这种情况下,您不需要在移动赋值运算符中再次删除它。 – Flynsee

+0

这就是它,我意识到我的错误,并更新了回应! :) –