C++ 重载运算符=在单链表中

C++ 重载运算符=在单链表中,c++,C++,我当前在将内容从一个列表复制到另一个列表时遇到问题。编译时,所有值都显示为0。我认为当复制构造函数正在使用其代码时,重载赋值运算符可能有问题 复制构造函数 ListOfDoubles::ListOfDoubles(const ListOfDoubles&e):head(NULL) { if (this != &e) { *this = e; } } 重载运算符= const ListOfDoubles& ListOfDoubles::operator=(cons

我当前在将内容从一个列表复制到另一个列表时遇到问题。编译时,所有值都显示为0。我认为当复制构造函数正在使用其代码时,重载赋值运算符可能有问题

复制构造函数

ListOfDoubles::ListOfDoubles(const ListOfDoubles&e):head(NULL) {
if (this != &e) {
    *this = e;
}
}
重载运算符=

const ListOfDoubles& ListOfDoubles::operator=(const ListOfDoubles 
&doubleslist)
{
DoubleListNode *cpyPtr = NULL;
DoubleListNode* orgPtr = doubleslist.head;

if (this != &doubleslist)
{
    while (head != NULL)
    {
        ListOfDoubles::~ListOfDoubles();
    }

    while (orgPtr != NULL)
    {
        if (head == NULL)
        {
            head = cpyPtr = new DoubleListNode(orgPtr->data);

        }
        else
        {
            cpyPtr->next = new DoubleListNode(orgPtr->data);
            cpyPtr = cpyPtr->next;
        }
        orgPtr = orgPtr->next;
    }
}
return *this;
}

通用复制逻辑类似于:

DoubleListNode * from = source.head; // copying from
DoubleListNode ** to = &head; // pointer to where we want to copy to
while (from != nullptr) // keep going until end of list. You did mark 
                        // the end of the list, didn't you?
{
    *to = new node(*from); //copy construct a new node around from and store it at to
    to = &(*to)->next; // advance to
    from = from.next; // advance from
}
*to = nullptr; // all done. Terminate list.
真正的魔力就在这里的双指针上:
DoubleListNode**to
通过有一个指向指针的指针,我们不在乎是指向
,还是指向另一个节点的
下一个
,或者你有什么。这只是另一个节点,因此没有特殊情况需要说明

您可以在复制构造函数和赋值运算符中执行上述操作,但最好不要重复自己的操作,并将其放入由复制构造函数和赋值运算符调用的函数中。请注意,对于复制构造函数中预先存在的数据(例如,列表将为空)和赋值运算符(例如,列表可能不为空,请在开始之前清除它并释放所有节点),需要考虑不同的假设


如上文评论所述,主要的替代方案是使用。因此,上面的复制循环只存在于复制构造函数中

通用复制逻辑类似于:

DoubleListNode * from = source.head; // copying from
DoubleListNode ** to = &head; // pointer to where we want to copy to
while (from != nullptr) // keep going until end of list. You did mark 
                        // the end of the list, didn't you?
{
    *to = new node(*from); //copy construct a new node around from and store it at to
    to = &(*to)->next; // advance to
    from = from.next; // advance from
}
*to = nullptr; // all done. Terminate list.
真正的魔力就在这里的双指针上:
DoubleListNode**to
通过有一个指向指针的指针,我们不在乎是指向
,还是指向另一个节点的
下一个
,或者你有什么。这只是另一个节点,因此没有特殊情况需要说明

您可以在复制构造函数和赋值运算符中执行上述操作,但最好不要重复自己的操作,并将其放入由复制构造函数和赋值运算符调用的函数中。请注意,对于复制构造函数中预先存在的数据(例如,列表将为空)和赋值运算符(例如,列表可能不为空,请在开始之前清除它并释放所有节点),需要考虑不同的假设


如上文评论所述,主要的替代方案是使用。因此,上面的复制循环只存在于复制构造函数中

复制构造函数的输入参数永远不会是正在构造的对象,因此请检查
this!=&复制构造函数中的e
是多余的

此外,直接手动调用析构函数是非法的,除非内存分配了
placement new
,而您没有使用该内存。您需要使用
delete
销毁节点实例

通常,您不应该按照
操作符=
实现复制构造函数,而应该反过来实现。让复制构造函数完成复制源值的工作,然后让
operator=
复制源列表并获得复制数据的所有权。这通常被称为“复制并交换”成语

请尝试以下方法:

ListOfDoubles::ListOfDoubles()
    : head(NULL)
{
}

ListOfDoubles::ListOfDoubles(const ListOfDoubles &e)
    : head(NULL)
{
    DoubleListNode *cpyPtr = NULL;
    DoubleListNode *prevPtr = NULL;
    DoubleListNode *orgPtr = e.head;

    while (orgPtr)
    {
        cpyPtr = new DoubleListNode(orgPtr->data);

        if (!head)
            head = cpyPtr;

        if (prevPtr)
            prevPtr->next = cpyPtr;
        prevPtr = cpyPtr;

        orgPtr = orgPtr->next;
    }

    /* alternatively:

    DoubleListNode **cpyPtr = &head;
    DoubleListNode *orgPtr = e.head;

    while (orgPtr)
    {
        *cpyPtr = new DoubleListNode(orgPtr->data);
        cpyPtr = &((*cpyPtr)->next);    
        orgPtr = orgPtr->next;
    }

    *cpyPtr = NULL;
    */
}

ListOfDoubles::~ListOfDoubles()
{
    DoubleListNode *orgPtr = head;
    DoubleListNode *nextPtr;

    while (orgPtr)
    {
        nextPtr = orgPtr->next;
        delete orgPtr;
        orgPtr = nextPtr;
    }
}

ListOfDoubles& ListOfDoubles::operator=(const ListOfDoubles &doubleslist)
{
    if (this != &doubleslist)
    {
        ListOfDouble tmp(doubleslist);
        std::swap(head, tmp.head);
    }
    return *this;
}

复制构造函数的输入参数永远不会是正在构造的对象,因此请检查
this!=&复制构造函数中的e
是多余的

此外,直接手动调用析构函数是非法的,除非内存分配了
placement new
,而您没有使用该内存。您需要使用
delete
销毁节点实例

通常,您不应该按照
操作符=
实现复制构造函数,而应该反过来实现。让复制构造函数完成复制源值的工作,然后让
operator=
复制源列表并获得复制数据的所有权。这通常被称为“复制并交换”成语

请尝试以下方法:

ListOfDoubles::ListOfDoubles()
    : head(NULL)
{
}

ListOfDoubles::ListOfDoubles(const ListOfDoubles &e)
    : head(NULL)
{
    DoubleListNode *cpyPtr = NULL;
    DoubleListNode *prevPtr = NULL;
    DoubleListNode *orgPtr = e.head;

    while (orgPtr)
    {
        cpyPtr = new DoubleListNode(orgPtr->data);

        if (!head)
            head = cpyPtr;

        if (prevPtr)
            prevPtr->next = cpyPtr;
        prevPtr = cpyPtr;

        orgPtr = orgPtr->next;
    }

    /* alternatively:

    DoubleListNode **cpyPtr = &head;
    DoubleListNode *orgPtr = e.head;

    while (orgPtr)
    {
        *cpyPtr = new DoubleListNode(orgPtr->data);
        cpyPtr = &((*cpyPtr)->next);    
        orgPtr = orgPtr->next;
    }

    *cpyPtr = NULL;
    */
}

ListOfDoubles::~ListOfDoubles()
{
    DoubleListNode *orgPtr = head;
    DoubleListNode *nextPtr;

    while (orgPtr)
    {
        nextPtr = orgPtr->next;
        delete orgPtr;
        orgPtr = nextPtr;
    }
}

ListOfDoubles& ListOfDoubles::operator=(const ListOfDoubles &doubleslist)
{
    if (this != &doubleslist)
    {
        ListOfDouble tmp(doubleslist);
        std::swap(head, tmp.head);
    }
    return *this;
}

ListOfDoubles::~ListOfDoubles()坏主意。你几乎从来都不想自己调用析构函数,这看起来也不罕见。它会导致未定义的行为调用析构函数,然后继续使用该对象。你完全是在倒行逆施;如果你想在CC和op=之间共享代码,那么就正确地实现CC,让op=复制并交换代码。这对你来说可能有些过分,但编写起来相当容易,证明起来也非常容易:@FrançoisAndrieux Holy smurf。这合法吗?哇。
ListOfDoubles::~ListOfDoubles()坏主意。你几乎从来都不想自己调用析构函数,这看起来也不罕见。它会导致未定义的行为调用析构函数,然后继续使用该对象。你完全是在倒行逆施;如果你想在CC和op=之间共享代码,那么就正确地实现CC,让op=复制并交换代码。这对你来说可能有些过分,但编写起来相当容易,证明起来也非常容易:@FrançoisAndrieux Holy smurf。这合法吗?哇!