C++ 运算符=链接列表问题的重载
我对链表的重载=运算符的实现有问题。列表类包含一个C++ 运算符=链接列表问题的重载,c++,linked-list,operator-overloading,C++,Linked List,Operator Overloading,我对链表的重载=运算符的实现有问题。列表类包含一个节点*头指针和一个结构节点,其中包含T*数据和节点*下一个,其中T是模板类型名。我对运算符函数结尾处发生的事情感到不安,在这里,析构函数(在本例中由makeEmpty处理)被运算符结尾调用了两次,一次是在遍历列表并创建相同节点的新列表之后,一次是在运算符函数退出之后 以下是makeEmpty实现: // Does the work of the Destructor template <typename T> void List<
节点*头
指针和一个结构节点
,其中包含T*数据
和节点*下一个
,其中T是模板类型名。我对运算符函数结尾处发生的事情感到不安,在这里,析构函数(在本例中由makeEmpty
处理)被运算符结尾调用了两次,一次是在遍历列表并创建相同节点的新列表之后,一次是在运算符函数退出之后
以下是makeEmpty实现:
// Does the work of the Destructor
template <typename T>
void List<T>::makeEmpty() {
cout << endl << endl << "DESTRUCTOR CALLED" << endl << endl;
List<T>::Node* tempPtr = head;
if (head != NULL) {
List<T>::Node* nextPtr = head->next;
for(;;) {
if (tempPtr != NULL) {
delete tempPtr->data;
tempPtr = nextPtr;
if (nextPtr != NULL)
nextPtr = nextPtr->next;
/*tempPtr = head->next;
delete head;
head = tempPtr;*/
}
else break;
}
}
}
// Overloaded to be able to assign one list to another
template <typename T>
List<T> List<T>::operator=(const List& listToCopy) {
List<T> listToReturn;
listToReturn.head = NULL;
List<T>::Node* copyPtr = listToCopy.head;
List<T>::Node* thisPtr = head;
if (copyPtr != NULL && thisPtr != NULL) {
for(;;) {
if (copyPtr != NULL) {
T* toInsert = new T(*copyPtr->data);
listToReturn.insert(toInsert);
copyPtr = copyPtr->next;
}
else{cout << endl << listToReturn << endl << endl; return listToReturn;}
}
}
// if right-hand list is NULL, return an empty list
return listToReturn;
}
//析构函数的工作
模板
void List::makeEmpty(){
数据);
listToReturn.insert(toInsert);
copyPtr=copyPtr->next;
}
否则{cout你要的是指针和提示,所以我给它:
1)不必要的动态数据成员
您的List::Node
不需要底层数据值的动态成员。它应该可以从const T&
构造,如果实现符合C++11的移动构造习惯用法,也可以使用T&
构造。并且两者都应该将next
成员初始化为nullptr
2)必须为列表
复制构造函数
根据,您的类具有动态成员,因此必须在复制构造和赋值操作符操作中正确管理它们(或隐藏所述实现,但显然这不是您的选项,因为它是您赋值的一部分)
3)将类复制构造函数用于赋值运算符重载
重载涉及动态分配的赋值运算符(它们就在这里,因为节点的链接列表要求这样做)理想情况下,应该利用复制构造函数和复制/交换习惯用法进行生命周期管理。这有许多好处,其中最重要的两个好处是通过优化编译器潜在地避免复制操作,以及将对象保持在其原始状态的异常安全性
4)List::operator=
覆盖应该返回对当前对象的引用
指定给的当前对象(操作符的左侧)应该是按引用返回的结果。应该是修改的对象。这是此类运算符的标准实现的一部分。您返回一个副本,但原始对象保持不变,从而完全违背赋值运算符的目的(即,在您的实现中,左值端实际上根本没有被修改)
下面将详细介绍其中的每一项:
不必要的动态数据成员
由于它没有发布,我必须在列表
中说一些安慰的话。我想象这样的事情:
template<class T>
class List
{
private:
struct Node
{
T* data;
Node* next;
};
Node *head;
// other members and decls...
};
List<int> lst1, lst2;
lst1.insert(1);
lst2.insert(2);
lst1 = lst2; // <== this line
现在,当需要一个新节点来保存T
对象时(例如在插入操作中),可以执行以下操作:
template<typename T>
void List<T>::someFunction(const T& obj)
{
Node *p = new Node(obj);
// ...use p somewhere...
}
我相信你看到这个会想,“嗯??”仔细看一下参数>代码> BYVA<代码>,并考虑为什么我这样命名它。它不是一个传统的<代码> const 引用,您可能已经习惯了。它是赋值表达式右手边的值副本。因此,为了创建它,编译器将生成一个新的<代码>。List
,调用copy构造函数来执行此操作。该复制的结果是临时对象byval
,我们将其作为参数。我们所做的就是交换头指针。想想这会做什么。通过交换头指针,我们得到他的列表,他得到我们的列表。但他的是赋值表达式原始右侧的副本,以及我们的,好吧,我们希望它被删除。这正是当byval
的析构函数在这个函数完成后被激发时会发生的事情
简言之,它生成如下代码:
template<class T>
class List
{
private:
struct Node
{
T* data;
Node* next;
};
Node *head;
// other members and decls...
};
List<int> lst1, lst2;
lst1.insert(1);
lst2.insert(2);
lst1 = lst2; // <== this line
列表lst1、lst2;
lst1.插入(1);
lst2.插入(2);
lst1=lst2;//您要求提供提示和提示,因此我给出:
1)不必要的动态数据成员
您的List::Node
不需要底层数据值的动态成员。它应该可以从const T&
构造,如果实现符合C++11的移动构造习惯用法,也可以使用T&
构造。并且两者都应该将next
成员初始化为nullptr
2)必须为列表
复制构造函数
根据,您的类具有动态成员,因此必须在复制构造和赋值操作符操作中正确管理它们(或隐藏所述实现,但显然这不是您的选项,因为它是您赋值的一部分)
3)将类复制构造函数用于赋值运算符重载
重载涉及动态分配的赋值运算符(它们就在这里,因为节点的链接列表要求这样做)理想情况下,应该利用复制构造函数和复制/交换习惯用法进行生命周期管理。这有许多好处,其中最重要的两个好处是通过优化编译器潜在地避免复制操作,以及将对象保持在其原始状态的异常安全性
4)List::operator=
覆盖应该返回对当前对象的引用
指定给的当前对象(操作符的左侧)应该是按引用返回的结果。它应该是被修改的对象。这是此类运算符的标准实现的一部分。您返回一个副本,但原始对象保持不变,从而完全违背赋值运算符的目的(即左值端)
List<int> lst1, lst2;
lst1.insert(1);
lst2.insert(2);
lst1 = lst2; // <== this line