为什么在函数内用 new 关键字声明实例指针会导致问题和未定义的行为?
Why does declaring an instance pointer with new keyword inside a function causes problems and undefined behaviour?
我正在学习 C++,在解决 HackerRank 平台上的挑战时遇到了一些有趣的事情。代码来自链表问题。目的是在Solution
class中用display
方法显示链表。我注意到我必须在初始化 class 实例时使用 new 关键字才能工作,否则代码不会检测到列表的末尾。请看下面两段代码及其各自的输出。
为什么在第二个代码示例中没有使用 new 关键字时,无法正确检测到链表的末尾?我在网上看到不鼓励使用 new 因为之后需要明确删除指针以避免内存泄漏,所以我很困惑。
两个代码使用相同的以下输入:
输入
4
2
3
4
1
代码 1
#include <iostream>
#include <cstddef>
using namespace std;
class Node
{
public:
int data;
Node *next;
Node(int d){
data=d;
next=NULL;
}
};
class Solution{
public:
Node* insert(Node *head,int data)
{
//Complete this method
Node* new_node = new Node(data); //Check this line
if (head) {
Node *current = head;
while(current->next) {
current = current->next;
}
current->next = new_node;
}
else
{
head = new_node;
}
// Node** pp = &head;
// while (*pp) {
// pp = &((*pp)->next);
// }
// *pp = new Node(data);
return head;
}
void display(Node *head)
{
Node *start=head;
while(start)
{
cout<<start->data<<" ";
start=start->next;
}
}
};
int main()
{
Node* head=NULL;
Solution mylist;
int T,data;
cin>>T;
while(T-->0){
cin>>data;
head=mylist.insert(head,data);
}
mylist.display(head);
}
输出 1(正确)
2 3 4 1
代码 2
#include <iostream>
#include <cstddef>
using namespace std;
class Node
{
public:
int data;
Node *next;
Node(int d){
data=d;
next=NULL;
}
};
class Solution{
public:
Node* insert(Node *head,int data)
{
//Complete this method
Node new_node(data);
if (head) {
Node *current = head;
while(current->next) {
current = current->next;
}
current->next = &new_node;
}
else
{
head = &new_node;
}
// Node** pp = &head;
// while (*pp) {
// pp = &((*pp)->next);
// }
// *pp = new Node(data);
return head;
}
void display(Node *head)
{
Node *start=head;
while(start)
{
cout<<start->data<<" ";
start=start->next;
}
}
};
int main()
{
Node* head=NULL;
Solution mylist;
int T,data;
cin>>T;
while(T-->0){
cin>>data;
head=mylist.insert(head,data);
}
mylist.display(head);
}
输出 2(不正确)
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1....
虽然使用 new
确实需要显式释放,但这也意味着如果您不使用 new
,那么对象的生命周期已经以某种方式隐式管理。
这是你的第二种情况:
Node new_node(data);
head = &new_node;
这里你实际上是获取局部变量的地址并将其分配给全局变量。但是当函数 returns 和您存储的地址不再有效时,局部变量本身将不复存在。
当您使用 new
时,对象的内存分配在一个独立的堆中,并且只要您不 delete
它就会一直存在。
PS。在 C++ 中,您可以(并且应该)使用 nullptr
而不是 NULL
如前所述,您的数据结构指向一个本地对象,该对象在函数返回时已经超出范围。访问 non-existent 对象会导致未定义的行为。
您似乎试图避免使用 new
。避免显式使用 new
的一种方法是使用智能指针来表示您的 Node
.
基于 unique_ptr
的解决方案可能类似于:
struct Node;
using NodePtr = std::unique_ptr<Node>;
因此,您可以使用 make_unique
而不是调用 new
。
class Node {
friend class Solution;
int data_;
NodePtr next_;
static NodePtr make (int d) {
return std::make_unique<Node>(d);
}
public:
Node (int d) : data_(d) {}
};
为避免搜索最后一个元素,您可以始终在列表中维护对最后一个元素的引用。我在 std::reference_wrapper
周围实现了一个名为 NodeRef
的垫片,使其更像一个指针。
class Solution {
NodePtr head_;
NodeRef tail_;
public:
Solution () : tail_(head_) {}
void insert (int d) {
*tail_ = Node::make(d);
tail_ = tail_->next_;
}
void display () {
NodeRef p = head_;
while (p) {
std::cout << p->data_ << ' ';
p = p->next_;
}
}
};
我正在学习 C++,在解决 HackerRank 平台上的挑战时遇到了一些有趣的事情。代码来自链表问题。目的是在Solution
class中用display
方法显示链表。我注意到我必须在初始化 class 实例时使用 new 关键字才能工作,否则代码不会检测到列表的末尾。请看下面两段代码及其各自的输出。
为什么在第二个代码示例中没有使用 new 关键字时,无法正确检测到链表的末尾?我在网上看到不鼓励使用 new 因为之后需要明确删除指针以避免内存泄漏,所以我很困惑。
两个代码使用相同的以下输入:
输入
4
2
3
4
1
代码 1
#include <iostream>
#include <cstddef>
using namespace std;
class Node
{
public:
int data;
Node *next;
Node(int d){
data=d;
next=NULL;
}
};
class Solution{
public:
Node* insert(Node *head,int data)
{
//Complete this method
Node* new_node = new Node(data); //Check this line
if (head) {
Node *current = head;
while(current->next) {
current = current->next;
}
current->next = new_node;
}
else
{
head = new_node;
}
// Node** pp = &head;
// while (*pp) {
// pp = &((*pp)->next);
// }
// *pp = new Node(data);
return head;
}
void display(Node *head)
{
Node *start=head;
while(start)
{
cout<<start->data<<" ";
start=start->next;
}
}
};
int main()
{
Node* head=NULL;
Solution mylist;
int T,data;
cin>>T;
while(T-->0){
cin>>data;
head=mylist.insert(head,data);
}
mylist.display(head);
}
输出 1(正确)
2 3 4 1
代码 2
#include <iostream>
#include <cstddef>
using namespace std;
class Node
{
public:
int data;
Node *next;
Node(int d){
data=d;
next=NULL;
}
};
class Solution{
public:
Node* insert(Node *head,int data)
{
//Complete this method
Node new_node(data);
if (head) {
Node *current = head;
while(current->next) {
current = current->next;
}
current->next = &new_node;
}
else
{
head = &new_node;
}
// Node** pp = &head;
// while (*pp) {
// pp = &((*pp)->next);
// }
// *pp = new Node(data);
return head;
}
void display(Node *head)
{
Node *start=head;
while(start)
{
cout<<start->data<<" ";
start=start->next;
}
}
};
int main()
{
Node* head=NULL;
Solution mylist;
int T,data;
cin>>T;
while(T-->0){
cin>>data;
head=mylist.insert(head,data);
}
mylist.display(head);
}
输出 2(不正确)
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1....
虽然使用 new
确实需要显式释放,但这也意味着如果您不使用 new
,那么对象的生命周期已经以某种方式隐式管理。
这是你的第二种情况:
Node new_node(data);
head = &new_node;
这里你实际上是获取局部变量的地址并将其分配给全局变量。但是当函数 returns 和您存储的地址不再有效时,局部变量本身将不复存在。
当您使用 new
时,对象的内存分配在一个独立的堆中,并且只要您不 delete
它就会一直存在。
PS。在 C++ 中,您可以(并且应该)使用 nullptr
而不是 NULL
如前所述,您的数据结构指向一个本地对象,该对象在函数返回时已经超出范围。访问 non-existent 对象会导致未定义的行为。
您似乎试图避免使用 new
。避免显式使用 new
的一种方法是使用智能指针来表示您的 Node
.
基于 unique_ptr
的解决方案可能类似于:
struct Node;
using NodePtr = std::unique_ptr<Node>;
因此,您可以使用 make_unique
而不是调用 new
。
class Node {
friend class Solution;
int data_;
NodePtr next_;
static NodePtr make (int d) {
return std::make_unique<Node>(d);
}
public:
Node (int d) : data_(d) {}
};
为避免搜索最后一个元素,您可以始终在列表中维护对最后一个元素的引用。我在 std::reference_wrapper
周围实现了一个名为 NodeRef
的垫片,使其更像一个指针。
class Solution {
NodePtr head_;
NodeRef tail_;
public:
Solution () : tail_(head_) {}
void insert (int d) {
*tail_ = Node::make(d);
tail_ = tail_->next_;
}
void display () {
NodeRef p = head_;
while (p) {
std::cout << p->data_ << ' ';
p = p->next_;
}
}
};