我有class A
和main()
函数:
class A {
public: int num;
public: A* parent;
A(){};
A::A (const A &s)
{
this->num = s.num;
this->parent = s.parent;
}
public : vector <A> foo(A a)
{
A a1;
a1.num = a.num;
a1.parent = &a;
vector <A> list;
list.reserve(1);
list.push_back(a1);
A temp = list.front();
cout << temp.parent->num << endl;
return list;
}
};
int main()
{
A a;
a.num =2;
vector <A> list = a.foo(a);
A temp = list.front();
cout << temp.parent->num << endl;
return 0;
}
问题是foo()
中的cout
按预期打印2,但main()
中的cout
打印了一个巨大的数字。
我认为问题是与内存重新分配有关的东西,因为我从多个来源读取,所以我尝试使用reserve()
,但它不起作用。
在foo()
中,您通过值传递a
参数。这意味着main()
传递给foo()
的A
对象将复制到a
参数中。a
参数本身是foo()
的局部变量。因此,a1.parent
被设置为指向一个局部变量。
然后list.push_back(a1);
对a1
进行复制,复制parent
指针。然后你做另一个复制当保存list.front()
的结果到temp
。
因此,对foo()
内部的temp.parent
指针解引用是完全有效的,因为parent
指向的a
在内存中仍然有效。
然而,一旦foo()
退出,a
被销毁,留下复制的parent
指针悬空,指向现在无效的内存。因此,在main()
中,解引用parent
指针是未定义行为,因为该指针不再指向原始a
对象。
如果你为A
对象的构造和销毁添加一些日志记录,你可以很容易地看到这一点,例如:
class A {
public:
int num = 0;
A* parent = nullptr;
A(){ cout << "A created: " << this << endl; }
A(const A &s) : num(s.num), parent(s.parent)
{
cout << "A copied: " << &s << " -> " << this << endl;
}
~A(){ cout << "A destroyed: " << this << endl; }
...
};
在线演示你会看到类似这样的记录:
A created: 0x7ffe5e735250 // 'a' in main() is created
A copied: 0x7ffe5e735250 -> 0x7ffe5e735260 // 'a' in foo() is passed in by main()
A created: 0x7ffe5e7351e0 // 'a1' in foo() is created
A copied: 0x7ffe5e7351e0 -> 0x5592f50b2e80 // 'a1' is copied into `list` in foo()
A copied: 0x5592f50b2e80 -> 0x7ffe5e7351f0 // list.front() is copied into 'temp' in foo()
(foo) temp.parent: 0x7ffe5e735260, num: 2 // parent is pointing at 'a' in foo()
// foo() exits, 'list' is transferred to main()
A destroyed: 0x7ffe5e7351f0 // 'temp' in foo() is destroyed
A destroyed: 0x7ffe5e7351e0 // 'a1' in foo() is destroyed
A destroyed: 0x7ffe5e735260 // 'a' in foo() is destroyed !!!!!
A copied: 0x5592f50b2e80 -> 0x7ffe5e735260 // main() reuses freed memory for 'temp' in main() !!!!!
(main) temp.parent: 0x7ffe5e735260, num: 2 // parent is pointing at the wrong A object !!!!!
// main exits
A destroyed: 0x7ffe5e735260 // 'temp' in main() is destroyed
A destroyed: 0x5592f50b2e80 // `list` in main() is destroyed
A destroyed: 0x7ffe5e735250 // 'a' in main() is destroyed
为了解决这个问题,将foo()
改为通过引用来接受A
对象(同时也将两个temp
变量更改为A&
引用):
vector<A> foo(A &a)
这样,在main()
中,只要传递给foo()
的本地a
对象仍然有效,返回的parent
指针将保持有效。
在线演示现在,您将看到类似这样的内容被记录:
A created: 0x7ffd7319e8b0 // 'a' in main() is created
// 'a' in foo() is passed in by main()
A created: 0x7ffd7319e850 // 'a1' in `foo() is created
A copied: 0x7ffd7319e850 -> 0x556ccff0fe80 // 'a1' is copied into 'list' in foo()
(foo) temp.parent: 0x7ffd7319e8b0, num: 2 // parent is pointing at 'a' in main()
// foo() exits, 'list' is transferred to main()
A destroyed: 0x7ffd7319e850 // 'a1' in foo() is destroyed
(main) temp.parent: 0x7ffd7319e8b0, num: 2 // parent is still pointing at 'a' in main()
// main() exits
A destroyed: 0x556ccff0fe80 // 'list' in main() is destroyed.
A destroyed: 0x7ffd7319e8b0 // 'a' in main() is destroyed !!!!!