我正在用 C++ 进行编程,在处理包含指向父类的指针的子类时,我遇到了取消引用的指针异常。简化的代码是这样的:
class top {
public:
int val = 0;
class mid {
public:
top* parent;
class low {
public:
mid* parent = nullptr;
void addToVal() {
parent->parent->val += 1;
}
low(mid* Parent) {
parent = Parent;
}
low() {}
};
low child;
mid(top* Parent) {
parent = Parent;
child = low(this);
}
};
mid::low child;
top() {
child = (mid(this)).child;
};
};
int main()
{
top t = top();
t.child.addToVal();
}
但是该代码的要点是,您有三个类,顶级类尝试访问中间类的子级,然后最低类尝试访问顶级类中的值。我知道错误来自这两行:
mid::low child;
和 child =(mid(this)).child;
但我不知道为什么执行此操作后该指针会被取消引用,因为指向父级的指针是在调用中间构造函数时定义的,如mid 构造函数也会调用 low 构造函数。有谁知道创建孩子时这里发生了什么?
通过 GCC 使用
-fsanitize=address
运行它,得到:
=================================================================
==1==ERROR: AddressSanitizer: stack-use-after-return on address 0x79bea1c00060 at pc 0x0000004012be bp 0x7ffce7648020 sp 0x7ffce7648018
READ of size 8 at 0x79bea1c00060 thread T0
#0 0x4012bd in top::mid::low::addToVal() /app/example.cpp:11
#1 0x40120f in main /app/example.cpp:31
#2 0x79bea3a29d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
#3 0x79bea3a29e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
#4 0x4010c4 in _start (/app/output.s+0x4010c4) (BuildId: 52657344baa840d03bd8657b741b84c6bfb25e30)
Address 0x79bea1c00060 is located in stack of thread T0 at offset 32 in frame
#0 0x401541 in top::top() /app/example.cpp:25
This frame has 1 object(s):
[32, 48) '<unknown>' <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /app/example.cpp:11 in top::mid::low::addToVal()
问题的原因是
mid(this)
是暂时的。当它被构造时,它将一个指向自身的指针传递给 low
,并且需要该指针才能通过 top
访问 parent->parent
实例。
因此,将其复制到
top::child
后,临时 mid
值将被销毁,从而留下存储在 child.parent
中的悬空指针。尝试 top().child.addToVal()
然后尝试使用这个悬空指针,这会导致未定义的行为。
你应该重新考虑你的设计。如果
low
需要指向 top
的指针,并且 mid
通常是临时的,那么只需直接存储它,而不是存储 mid
:
class low {
public:
top* parent = nullptr;
void addToVal() {
parent->val += 1;
}
low(mid* Parent) {
parent = Parent->parent;
}
low() {}
};