我有两个虚拟问题让我困惑了一段时间。我确实在网上进行了一些搜索并通读了很多 C++ 教程,但是我找不到具体的答案。
假设我们有一个名为 Node 的类,它是单向链表的构建 block 。
class Node
{
int data;
Node* next;
}
事实1:局部变量(非静态)将在相应函数退出时被销毁。
问题1:情况如何:
Node* func()
{
Node n;
Node* ptr=&n;
return n;
}
节点n会不会被销毁?或者我们必须使用 new 运算符来创建节点并返回指向堆内存的指针。如果两种方法都有效,哪种方法更好?
问题2:如何为节点类编写析构函数? (我在 stackOverflow 上发现了一些类似的问题,但这些答案集中在链表的析构函数上。我已经知道了那部分。我想要的正是 Node 类的析构函数)。
------------------------------------编辑------ ------------------------------
感谢所有给我建议或指出我错误的人。我想我得到了答案。下面是我根据您的回答做的笔记,它确实消除了我的困惑。
- 从函数返回堆栈内存地址不是一个好习惯,因为它会导致未定义的行为。
- 返回堆内存是可以的,但我们必须注意对象的销毁。
- 另一种方法是返回一个对象,受益于复制构造函数。
最佳答案
问题一
Node* func() { Node n; Node* ptr=&n; return n;}
您的代码创建一个本地 Node
实例(在堆栈上),然后返回它的地址。当函数返回时,作为局部变量的 Node
实例被销毁。函数返回的地址现在指向一些具有未定义内容的内存,任何取消引用此指针的尝试都将导致未定义的行为。
为了创建节点,您实际上需要调用Node
构造函数。你想如何返回结果与你如何调用构造函数有关。
您可以像您尝试的那样返回一个指针,在这种情况下您需要使用
new
运算符:Node* func() { Node* n = new Node(10); return n; }
但是,当您这样做时,您就赋予了
func
调用者销毁相关对象的责任。由于new
和delete
是对称操作,因此将它们放在代码中的对称位置被认为是更好的形式,例如像这样:void cnuf(Node* p) { delete p; }
一个更好的替代方案可能是使用
std::shared_ptr
,它可以为您提供引用计数,如下所示:std::shared_ptr<Node> func() { return std::make_shared<Node>(10); }
使用这种方法,调用者不需要手动管理每个节点的生命周期。另一种替代方法是使用
std::unique_ptr
,它只允许单一对象所有权。或者你可以按值返回节点,在这种情况下你在本地创建它,然后让函数返回机制在你返回它时制作一个拷贝:
Node func() { Node n(10); return n; }
问题二
您可以在 Node
类声明中像这样声明一个析构函数:
class Node {
...
~Node();
}
然后,你可以这样定义它:
Node::~Node() {
...
}
但是,实际上让列表管理其 Node
实例(next
字段)之间的连接可能更好,并且只让 Node
类管理其成员数据的生命周期(data
字段)
关于C++:在函数中创建新对象并将其作为结果返回时,是否必须使用 new 运算符来创建对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26294543/