让我们考虑一下下面的代码。事实上,这是我使用 gmock 和模拟 void(void) 方法发现的缩小问题。
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base
{
public:
void GetValueAndDelete() { delete this; } //here we crash
};
int main() {
Derived* p = 0;
p->GetValueAndDelete();
}
构建它:
/tools/gcc6.1/bin/g++ --version
g++ (GCC) 6.1.0
优化级别不同于 -O0 且运行结果会导致分段错误。
是 gcc 错误还是带有 c++ 代码的东西(是的,是的,我知道它使用副作用,但它适用于其他编译器并且无需优化)
是 gcc 错误吗
不。
或带有 C++ 代码的内容
是的。在不指向有效对象的指针上使用箭头运算符。这具有未定义的行为。
取消引用空指针并以这种不使用任何成员的方式调用方法是可以的。
按照标准是不行的。是UB。
什么是指针上的调用方法?
是特定于实现的。
删除它没什么特别的。
删除this
是非常特别的。
您应该只注意在之后不使用任何成员
是的,您必须确保仅使用new
来创建调用该函数的所有实例。没有自动对象,没有静态对象没有new[]
,没有malloc
+放置新。
所以是的,你可以delete this
,但要小心。
你在这里的直觉似乎是:
-
p->
是可以的,因为它只是填写this
的语法。 -
delete this
是可以的,因为您在delete
之后不使用this
。
但上述内容实际上可能并不正常,因为取消引用空指针始终是未定义的行为。 如果没有启用优化,这可能不会造成任何麻烦,因为编译器正在执行"基本编译",这与您"在脑海中编译"时的想象有些匹配。 然而,通过优化,GCC会做很多它通常不会做的事情,比如在面对不正确的源代码时不费心发出"正确"的指令。 当你说p->
是程序的第一个行为时,它根本不需要做任何事情——它可以假装main()
是空的(或者崩溃,就像你的情况一样)。
我知道在 nullptr 上调用方法不符合标准,但到目前为止,我还没有看到一个编译器无法以描述的方式处理这个问题。似乎 gcc6.1 的工作方式不同(仍然根据规范)。所以这是gmock错误。我想知道有多少其他项目依赖于这种编译器行为:)
值得一提的是,该方法被调用,"this"内部按预期为零,然后删除失败。我查看了程序集,其中有 mov (%rax),%rbx,它失败了,因为 rax 包含零。