这是对我今天早些时候提出的关于直接调用类的析构函数的拨款的问题的跟进。
我正在为作业创建自己的列表。我重载了赋值、方括号、输入和输出流运算符,并具有基本的添加、删除和打印到屏幕功能。
我的下一步是让列表删除并释放其所有节点的内存,我遇到了麻烦。
我让 List 在执行操作后将其当前状态写入屏幕,如下所示:
void main()
{
// Create instance of list
List aList(true);
List bList(true);
aList.Add(1);
aList.Add(2);
bList.Add(2);
cout << "aList: " << aList;
cout << "bList: " << bList;
aList = bList;
cout << "aList: " << aList;
cout << "bList: " << bList;
aList.Add(3);
cout << "aList: " << aList;
cout << "bList: " << bList;
int i = 0; cin >> i;
}
这是我重载的输出流:
ostream &operator<<(ostream &stream, List theList)
{
stream << "There are " << theList._totalNodes << " nodes in the list." << endl;
for(int i = 0; i < theList._totalNodes; i++)
{
stream << "Node #" << i << " holds the data: " << theList[i] << endl;
}
return stream;
}
看这个使用后的命令提示符窗口下面还打印了“Destructor called”。
列表析构函数:
List::~List()
{
cout << "Destructor called" << endl;
// List::Destroy();
}
重载的赋值运算符似乎也调用了析构函数,这并不让我感到惊讶。但是,其他人会。
List &List::operator=(List &aList)
{
// Make sure not the same object trying to be copied
if(this != &aList)
{
// Perform deep copy
// Delete old pointers in redundant list, only if they exist already
if(_head)
{
delete _head, _tail, _current, _del;
_totalNodes = 0; // Reset total nodes
}
// Now set new pointers copied from other list
_head = NULL;
_tail = _head;
_current = _head;
_del = _head;
// Now loop through all nodes in other list and copy them to this
// Reset pointer in other list
aList.Reset();
for(int i = 0; i < aList._totalNodes; i++)
{
Add(aList.Current()->_data);
aList.Next();
}
// Reset other list's pointer
aList.Reset();
Reset(); // Reset our pointer
}
return *this; // Return for multi assignment
}
我也有一个 List 的析构函数,当它被调用时写上“Destructor called”,并注意到它在每次使用其中一种方法后都会被调用。
这是为什么?我假设当对象不再需要时调用析构函数,即删除。
此外,在单步执行我的代码时,我注意到当指针被删除时,它指向的内存地址并没有被取消。删除后是否必须手动将指针置空?
最佳答案
这是因为您按值获取了您的列表——也就是说,您复制了它。你需要通过 const 引用来获取它——也就是说,
ostream &operator<<(ostream &stream, const List& theList)
此外,您的赋值运算符应该采用常量引用,而不是非常量引用。
我也不认为这是
delete _head, _tail, _current, _del;
做你想做的事。它只是删除 _del
而其他人都没有。
您应该使用递归的 self 拥有设计,其中每个节点都会删除链中的下一个节点。编辑:由于不清楚,我将发布一个简短的示例。
template<typename T> struct Node {
Node(const T& arg, Node* prev) : t(arg), previous(prev) {}
T t;
std::auto_ptr<Node<T>> next;
Node<T>* previous;
};
template<typename T> class List {
std::auto_ptr<Node<T>> head;
Node<T>* tail;
public:
List() : head(NULL), tail(NULL) {}
void clear() { head = tail = NULL; } // Notice how simple this is
void push_back() {
push_back(T());
}
void push_back(const T& t) {
if (tail) {
tail = (tail->next = new Node<T>(t, tail)).get();
return;
}
tail = (head = new Node<T>(t, NULL)).get();
}
void pop_back() {
tail = tail->previous;
tail->next = NULL;
}
void pop_front() {
head = head->next;
}
List<T>& operator=(const List& ref) {
clear(); // Clear the existing list
// Copy. Gonna leave this part to you.
}
};
请注意我从未删除任何内容- std::auto_ptr
为我做了一切,操纵列表及其节点变得容易得多,因为 std::auto_ptr
管理所有涉及的内存。此列表甚至不需要自定义析构函数。
我建议您仔细阅读 std::auto_ptr
,它的接口(interface)有一些惊喜,例如“复制”构造函数和赋值运算符实际移动,您在使用该类之前会想知道这些。我希望我可以开始使用 std::unique_ptr
而不是所有时间,这会让生活变得更加轻松。
关于每次使用 List 的方法后调用 C++ List 析构函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5051340/