[英]How does returning pointer from a function work?
Consider the following code snippet: 考虑以下代码片段:
#include <iostream>
#include <queue>
struct node {
int key;
node *l;
node *r;
node(int key)
:key(key), l(nullptr), r(nullptr) {
}
};
struct bst {
node *root;
bst() :root(nullptr) {
}
node* find(int key) {
return find(root, key);
}
node* find(node *root, int key) {
if (!root) {
return nullptr;
} else {
if (root->key < key) {
return find(root->l, key);
}
else if (root->key > key) {
return find(root->r, key);
} else {
return root;
}
}
}
void insert(int key) {
insert(root, key);
}
void insert(node *&root, int key) {
if (!root) {
root = new node(key);
} else {
if (root->key < key) {
insert(root->r, key);
} else if (root->key > key) {
insert(root->l, key);
} else {
return;
}
}
}
void print_by_level(std::ostream &o) {
if (!root) {
o << "(empty)";
return;
}
std::queue<node*> q;
int curr_lv = 1;
int next_lv = 0;
q.push(root);
while (!q.empty()) {
auto p = q.front();
q.pop();
curr_lv--;
o << p->key << ' ';
if (p->l) {
q.push(p->l);
next_lv++;
}
if (p->r) {
q.push(p->r);
next_lv++;
}
if (curr_lv == 0) {
o << '\n';
curr_lv = next_lv;
next_lv = 0;
}
}
o << '\n';
}
};
int main() {
bst t;
t.insert(5);
t.insert(10);
t.insert(15);
t.print_by_level(std::cout);
// return pointer to 5 which is root
node *p = t.find(5);
// modify it, ok
p->key = 100;
t.print_by_level(std::cout);
// now try to delete or change where p is pointing to
delete p;
p = nullptr;
// then it's not happy :(
t.print_by_level(std::cout);
return 0;
}
I would expect p
returning from find()
is root
, however it's not the case! 我希望从
find()
返回的p
是root
,但是事实并非如此! It seemed like it only returned a copy of that pointer. 似乎它只返回了该指针的副本。 However
p->key = 100
actually changed the value of root
. 但是
p->key = 100
实际上更改了root
的值。 Could anyone help me explain this? 有人可以帮我解释一下吗?
On the other hand, if I delete t.root
manually then it worked as expected. 另一方面,如果我手动删除
t.root
,则它按预期工作。
int main() {
bst t;
t.insert(5);
t.insert(10);
t.insert(15);
t.print_by_level(std::cout);
// return pointer to 5 which is root
node *p = t.find(5);
// modify it, ok
p->key = 100;
t.print_by_level(std::cout);
delete t.root;
t.root = nullptr;
// ok happy now
t.print_by_level(std::cout);
return 0;
}
Now try to change where p
points to and return node*&
: (please forgive me for a dirty hack nullp
:(). 现在尝试更改
p
指向的位置并返回node*&
:(请原谅我一个肮脏的hack nullp
:()。
#include <iostream>
#include <queue>
struct node {
int key;
node *l;
node *r;
node(int key)
:key(key), l(nullptr), r(nullptr) {
}
};
struct bst {
node *root;
node *nullp;
bst() :root(nullptr), nullp(nullptr) {
}
node*& find(int key) {
return find(root, key);
}
node*& find(node *root, int key) {
if (!root) {
return nullp;
} else {
if (root->key < key) {
return find(root->l, key);
}
else if (root->key > key) {
return find(root->r, key);
} else {
return root;
}
}
}
void insert(int key) {
insert(root, key);
}
void insert(node *&root, int key) {
if (!root) {
root = new node(key);
} else {
if (root->key < key) {
insert(root->r, key);
} else if (root->key > key) {
insert(root->l, key);
} else {
return;
}
}
}
/**
* p q
* / \ / \
* q x3 => x1 p
* / \ / \
* x1 x2 x2 x3
*/
void rotate_w_left_child(node *&p) {
node *q = p->l;
p->l = q->r;
q->r = p;
p = q;
}
void print_by_level(std::ostream &o) {
if (!root) {
o << "(empty)";
return;
}
std::queue<node*> q;
int curr_lv = 1;
int next_lv = 0;
q.push(root);
while (!q.empty()) {
auto p = q.front();
q.pop();
curr_lv--;
o << p->key << ' ';
if (p->l) {
q.push(p->l);
next_lv++;
}
if (p->r) {
q.push(p->r);
next_lv++;
}
if (curr_lv == 0) {
o << '\n';
curr_lv = next_lv;
next_lv = 0;
}
}
o << '\n';
}
};
int main() {
bst t;
t.insert(5);
t.insert(4);
t.insert(1);
t.print_by_level(std::cout);
node *p = t.find(5);
// using root, happy
// t.rotate_w_left_child(t.root);
// t.print_by_level(std::cout);
// using p, not happy :(
t.rotate_w_left_child(p);
t.print_by_level(std::cout);
return 0;
}
In main
when you delete p
you are actually deleting the root
pointer in the t
object! 在
main
当delete p
时,实际上是在删除t
对象中的root
指针! Don't do that, or you will have undefined behavior, which is what you experience here. 请勿这样做,否则您将遇到不确定的行为,这就是您在此处所遇到的。
When you return a pointer, you return a copy of the pointer , both pointers will point to the same object. 当您返回一个指针,返回指针的副本,两个指针会指向同一个对象。 If you then
delete
one of the pointers, the other pointer will now point to a deleted object. 如果然后
delete
其中一个指针,则另一个指针现在将指向已删除的对象。
[This is not necessarily answer, but a comment with code] [这不一定是答案,而是带有代码的注释]
Your code may be suffering from Variable Shadowing, the member "root" clearly clashes with the function parameter "root". 您的代码可能受可变阴影的影响,成员“ root”显然与函数参数“ root”冲突。
A common practice to help avoid issues like this is to prefix members and other special cases with a prefix, such as "m_" for "member", "s_" for static, "g_" for "global". 避免此类问题的常见做法是为成员和其他特殊情况加上前缀,例如,“ m_”代表“ member”,“ s_”代表静态,“ g_”代表“ global”。 Don't confuse it with the dreaded hungarian - at least, not systems .
不要把它与可怕的匈牙利人混为一谈,至少不要与系统混为一谈。
struct bst {
node *m_root;
node *m_nullp;
bst() : m_root(nullptr), m_nullp(nullptr) {
}
node*& find(int key) {
return find(m_root, key);
}
node*& find(node *root, int key) {
if (!root) { // definitely means the parameter
return nullp;
} else {
if (root->key < key) {
return find(root->l, key);
}
else if (root->key > key) {
return find(root->r, key);
} else {
return root;
}
}
}
void insert(int key) {
insert(m_root, key);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.