C++中动态分配的单向链表的赋值运算符 (MS Visual Studio 2015)

Assignment Operator for singly linked list with dynamic allocation in C++ (MS Visual Studio 2015)

本文关键字:Visual MS Studio 2015 链表 动态分配 C++ 赋值运算符      更新时间:2023-10-16

两个类,一个用于节点,一个用于单向链表。

// node class
template <typename T>
class Element{
private:
Element *next_ = nullptr;
string name_ = "";
T color_ = T();
public:
Element()=default;
Element(string name, T d) : next_(nullptr), name_(name), color_(d){};
friend ostream& operator<<(ostream& out, Element& n){
out << n.name_ << ":" << n.color_;
return out;
}
friend class PAL<T>;
};
/* "This is a singly linked list of Elements that, taken in order,
constitute the Painter's ALgorithm." */
template<typename T>
class PAL{
private:
Element<T> *back_ = nullptr;
Element<T> *front_ = nullptr;
void print_list(ostream& out); 
public:
PAL()=default;
PAL(Element<T> n) : back_(&n), front_(&n) {};
PAL(string n, T d);
PAL(const PAL&);
~PAL();
PAL& operator=(PAL);
void add(Element<T> &n);
void add(string name, T dat);
pair<Element<T>*, Element<T>*> find(string name);    
pair<Element<T>*, Element<T>*> find(Element<T> &n);
void move_forward1(Element<T> &n);
void move_to_front(Element<T> &n);  
void move_back1(Element<T> &n);
void move_to_back(Element<T> &n);  
friend ostream& operator<<(ostream& out, PAL<T>& sl){
sl.print_list(out);
return out;
}

现在对于赋值运算符,我尝试了两件事。

template<typename T>
PAL<T> & PAL<T>::operator=(PAL p)
{
front_ = p.front_;
back_ = p.back_;
return *this;
}

以及使用 std::swap 的变体。 两者都编译,但给出运行时错误。

当我使用 swap 时,我最终会出现一个错误,我认为它说内存不足?

表达式:"(_Ptr_user & (_BIG_ALLOCATION_ALIGNMENT -1)) == 0" &&&0

如果我使用第一个,没有交换,就会发生奇怪的狗屎。我调试并观察了"this"和"p",因为它逐步执行了函数,它运行良好,直到最后,最后一个括号意味着什么,突然"p"和"this"不仅更改为nullptrs,而且更改为0xdddddd(我猜d的数量)。所以它有各种各样的问题。

我想知道为什么会发生这些事情,但如果没有别的,我需要知道我应该做什么!

非常感谢。

编辑:该功能现在如下。

template<typename T>
PAL<T> & PAL<T>::operator=(const PAL &p)
{
Element<T> *new_front = nullptr, *new_back = nullptr;
Element<T> *address = p.back_;
while (address != nullptr) {
/* the add method in this loop will end up taking
care of front_ and back_ too */
add(address->name_, address->color_);
pair<Element<T>*, Element<T>*> found = find(address->name_);
if (new_front == nullptr) {
new_front = found.first;
}
new_back = found.first;
//move_to_front(*found.first);
address = address->next_;
}

Element<T> *old = p.back_;
while (old) {
Element<T> *last = old;
old = old->next_;
delete last;
}
front_ = new_front;
back_ = new_back;

return *this;
}

请注意,back_表示列表中的"第一个"项(没有任何内容指向它),front_表示最后一个,它不指向任何内容。

复制赋值操作员的工作不仅仅是复制指针;默认编译器生成可以为您做到这一点。

此运算符的工作是另一个对象的内容复制到一个完全独立的对象中。 这意味着将每个Element<T>对象从另一个对象复制到此对象中。 这也意味着您必须处理销毁当前对象包含的任何节点。

只需像您所做的那样复制指针,就意味着对一个PAL<T>的更改可能会反映在另一个中。 如果头部或尾部发生变化,则只有一个PAL<T>会发生此更改。

更糟糕的是,如果您复制分配一个PAL<T>而不是另一个,您现在有两个PAL<T>对象认为自己拥有它们包含Element<T>对象。 然后,当两者都销毁时,它们将各自尝试删除这些对象。 这会导致双重释放,从而导致未定义的行为。

首先,更改运算符以引用常量而不是副本。

然后,您将要使用的常规模式如下所示:

template<typename T>
PAL<T> & PAL<T>::operator=(const PAL &p)
{
// Store the old list here, which we will deallocate at the end. This
// step is necessary to do first in case some code copy-assigns an
// instance to itself; since in that case it is copying its own data,
// we can't deallocate it yet or we will just lose the data completely.
Element<T> *old_back = back_;
// Reset this instance.
front_ = nullptr;
back_ = nullptr;
// Duplicate the elements of p, storing them in this object.
// You will have to make sure the next_ fields of the new elements point
// into the duplicated chain, not the original!
//
// Left as an exercise for you to implement.
// Deallocate and free the nodes that were contained in this object.
while (old_back) {
Element<T> *last = old_back;
old_back = old_back->next;
delete last;
}
return *this;
}

我猜你的复制构造函数也不会做这种复制。 如果没有,那么你可以用这个运算符来实现它,以尽量减少代码重复:

template<typename T>
PAL<T>::PAL(const PAL &p) : front_(nullptr), back_(nullptr) {
*this = p;
}