在 class 的析构函数中应删除的所有内容
What all should be deleted in the destructor of a class
所以我已经有一段时间没有完成任何 C++ 编码了,我只是想知道应该在析构函数中删除基本链表中的哪些变量,不幸的是我目前无法查阅我的 C++ 手册事。链表 class 如下所示:
#include <string>
#include <vector>
class Node
{
Node *next;
string sName;
vector<char> cvStuff;
Node(string _s, int _i)
{
next = nullptr;
sName = _s;
for (int i = 0; i < _i; i++)
{
cvStuff.insert(cvStuff.end(), '_');
}
}
~Node()
{
//since sName is assigned during runtime do I delete?
//same for cvStuff?
}
};
我也很好奇,如果在我调用的析构函数中
delete next;
是否会转到链表的下一个节点并删除该节点,从而递归地从该点删除整个列表?另外,如果是这种情况并且我出于某种原因选择实现它,我是否必须在删除它之前检查 next 是否为 nullptr 或者它不会有所作为吗?
谢谢。
基本上你应该
delete next
这就是你应该做的:
string
和vector
对象有自己的析构函数,由于这个对象正在被析构,theirs will be called.
Deleting a null pointer is not a problem,所以你甚至不必检查它。
如果next
不是空指针,它将根据需要继续调用下一个节点的析构函数。
删掉就行了
将调用您的 class 析构函数(它是空的),然后调用成员对象的析构函数。
如果成员不是对象,则不调用析构函数。
在你的例子中:
- List *next: pointer on List: no destructor called
- string sName: string object: destructor called
- vector<char> cvStuff: vector object: destructor called
好消息:你无事可做。析构函数声明在这里甚至没有用。
如果您在析构函数中删除 next
,则删除一个项目将删除列表中的所有其他项目:不是很有用。
(你的 List
对象应该被称为 Node
)。列表是所有节点的链接结果,由您创建的第一个节点持有。
理想情况下,什么都不做:使用智能指针:std::unique_ptr<>
、std::smart_ptr<>
、boost::scoped_ptr<>
等。
否则,您删除拥有的本机指针。 next
是否拥有?
- 您打算删除列表中间的内容吗?如果是,则不能在析构函数中删除。
- 你打算分享尾巴吗?如果是,则需要 reference-counted 个智能指针。
可以删除 nullptr(什么都不做)。在示例中,您不应该删除 sName 和 cvStuff,因为它们在范围内,因此会自动销毁。
此外,如果这将是一个可以变大的列表,您可能需要手动销毁和释放 *next
。这是因为您不想通过递归 运行 出栈 space。
此外,我建议将其分为List
,表示数据结构和ListNode
,表示元素。你的问题实际上表明了这种歧义,你不知道你是在析构函数中删除 ListNode
还是 List
。将它们分开可以解决这个问题。
具有自动生命周期的对象在超出范围时会调用其析构函数:
{ // scope
std::string s;
} // end scope -> s.~string()
动态对象(使用 new 分配)不会调用其析构函数,除非对其调用 delete
。
对于成员变量,范围是对象的生命周期。
struct S {
std::string str_;
char* p_;
};
int main() { // scope
{ // scope
S s;
} // end scope -> s.~S() -> str_.~string()
}
请注意,上面的 p_
没有发生任何特殊情况:它是一个简单标量类型的指针,因此代码不会自动对其执行任何操作。
所以在你的列表 class 中你唯一需要担心的是你的 next
成员:你需要决定它是否是一个 "owning" 指针。如果它是一个 "owning" 指针,那么您必须在析构函数中对对象调用 delete
。
或者,您可以利用 'RAII'(资源获取即初始化)并使用 对象 包装指针并提供将调用 [=15= 的析构函数] 给你:
{ // scope
std::unique_ptr<Node> ptr = std::make_unique<Node>(args);
} // end scope -> ptr.~unique_ptr() -> delete -> ~Node()
unique_ptr
是一个纯粹拥有的指针,另一种选择可能是 shared_ptr
,它使用引用计数,这样底层对象只有 delete
d 当你没有对象的任何剩余 shared_ptr
s。
如果您将 Node
的实际地址保存在其他地方:
,您会认为您的 next
指针是非拥有指针
std::vector<Node> nodes;
populate(nodes);
list.insert(&nodes[0]);
list.insert(&nodes[1]);
// ...
在上面的例子中,vector 拥有节点,你绝对不应该在 Node
析构函数中调用 delete
,因为 Node
不是你要删除的。
list.insert(new Node(0));
list.insert(new Node(1));
这里,list/Nodes 是唯一具有指向节点的指针的东西,所以在这个用例中我们需要 Node::~Node
来调用 delete 否则我们有泄漏。
所以我已经有一段时间没有完成任何 C++ 编码了,我只是想知道应该在析构函数中删除基本链表中的哪些变量,不幸的是我目前无法查阅我的 C++ 手册事。链表 class 如下所示:
#include <string>
#include <vector>
class Node
{
Node *next;
string sName;
vector<char> cvStuff;
Node(string _s, int _i)
{
next = nullptr;
sName = _s;
for (int i = 0; i < _i; i++)
{
cvStuff.insert(cvStuff.end(), '_');
}
}
~Node()
{
//since sName is assigned during runtime do I delete?
//same for cvStuff?
}
};
我也很好奇,如果在我调用的析构函数中
delete next;
是否会转到链表的下一个节点并删除该节点,从而递归地从该点删除整个列表?另外,如果是这种情况并且我出于某种原因选择实现它,我是否必须在删除它之前检查 next 是否为 nullptr 或者它不会有所作为吗?
谢谢。
基本上你应该
delete next
这就是你应该做的:
string
和vector
对象有自己的析构函数,由于这个对象正在被析构,theirs will be called.Deleting a null pointer is not a problem,所以你甚至不必检查它。
如果
next
不是空指针,它将根据需要继续调用下一个节点的析构函数。
删掉就行了
将调用您的 class 析构函数(它是空的),然后调用成员对象的析构函数。
如果成员不是对象,则不调用析构函数。
在你的例子中:
- List *next: pointer on List: no destructor called
- string sName: string object: destructor called
- vector<char> cvStuff: vector object: destructor called
好消息:你无事可做。析构函数声明在这里甚至没有用。
如果您在析构函数中删除 next
,则删除一个项目将删除列表中的所有其他项目:不是很有用。
(你的 List
对象应该被称为 Node
)。列表是所有节点的链接结果,由您创建的第一个节点持有。
理想情况下,什么都不做:使用智能指针:std::unique_ptr<>
、std::smart_ptr<>
、boost::scoped_ptr<>
等。
否则,您删除拥有的本机指针。 next
是否拥有?
- 您打算删除列表中间的内容吗?如果是,则不能在析构函数中删除。
- 你打算分享尾巴吗?如果是,则需要 reference-counted 个智能指针。
可以删除 nullptr(什么都不做)。在示例中,您不应该删除 sName 和 cvStuff,因为它们在范围内,因此会自动销毁。
此外,如果这将是一个可以变大的列表,您可能需要手动销毁和释放 *next
。这是因为您不想通过递归 运行 出栈 space。
此外,我建议将其分为List
,表示数据结构和ListNode
,表示元素。你的问题实际上表明了这种歧义,你不知道你是在析构函数中删除 ListNode
还是 List
。将它们分开可以解决这个问题。
具有自动生命周期的对象在超出范围时会调用其析构函数:
{ // scope
std::string s;
} // end scope -> s.~string()
动态对象(使用 new 分配)不会调用其析构函数,除非对其调用 delete
。
对于成员变量,范围是对象的生命周期。
struct S {
std::string str_;
char* p_;
};
int main() { // scope
{ // scope
S s;
} // end scope -> s.~S() -> str_.~string()
}
请注意,上面的 p_
没有发生任何特殊情况:它是一个简单标量类型的指针,因此代码不会自动对其执行任何操作。
所以在你的列表 class 中你唯一需要担心的是你的 next
成员:你需要决定它是否是一个 "owning" 指针。如果它是一个 "owning" 指针,那么您必须在析构函数中对对象调用 delete
。
或者,您可以利用 'RAII'(资源获取即初始化)并使用 对象 包装指针并提供将调用 [=15= 的析构函数] 给你:
{ // scope
std::unique_ptr<Node> ptr = std::make_unique<Node>(args);
} // end scope -> ptr.~unique_ptr() -> delete -> ~Node()
unique_ptr
是一个纯粹拥有的指针,另一种选择可能是 shared_ptr
,它使用引用计数,这样底层对象只有 delete
d 当你没有对象的任何剩余 shared_ptr
s。
如果您将 Node
的实际地址保存在其他地方:
next
指针是非拥有指针
std::vector<Node> nodes;
populate(nodes);
list.insert(&nodes[0]);
list.insert(&nodes[1]);
// ...
在上面的例子中,vector 拥有节点,你绝对不应该在 Node
析构函数中调用 delete
,因为 Node
不是你要删除的。
list.insert(new Node(0));
list.insert(new Node(1));
这里,list/Nodes 是唯一具有指向节点的指针的东西,所以在这个用例中我们需要 Node::~Node
来调用 delete 否则我们有泄漏。