Let's consider follwing code. In fact this is narrowed problem I found using gmock and mocking void(void) method.
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base
{
public:
void GetValueAndDelete() { delete this; } //here we crash
};
int main() {
Derived* p = 0;
p->GetValueAndDelete();
}
Building it with:
/tools/gcc6.1/bin/g++ --version
g++ (GCC) 6.1.0
with optimization level different than -O0 and running the result causes segmentation fault.
Is it gcc bug or something with c++ code (yes, yes, I know that it uses side effects, but it works with other compilers and without optimization as well)
No.
Yes. You use the arrow operator on a pointer that doesn't point to a valid object. This has undefined behaviour.
It's not fine according to the standard. It's UB.
Is implementation specific.
Deleting
this
is very special.Yes, that and you must make sure that only
new
was used to create all instances on which the function is ever called. No automatic objects, no static objects nonew[]
, nomalloc
+ placement new.So yes, you can
delete this
, but be careful.I know that calling method on nullptr is out of standard, but so far I haven't seen a compiler that wouldn't cope with that in described manner. It seems that gcc6.1 works different (still according to spec). So this is gmock bug. I wonder how many other projects depend on such compiler behavior :)
What is worth to mention is that method is called, 'this' inside is zero as expected and then delete fails. I looked into assembly an there is mov (%rax), %rbx which fails because rax contains zero.
Your intuition here seems to be:
p->
is OK because it is just a syntax for filling inthis
.delete this
is OK because you don't usethis
afterdelete
.But the above may not actually be OK, because dereferencing a null pointer is always undefined behavior. Without optimization enabled this may not cause any trouble, because the compiler is doing a "basic compile" which somewhat matches what you imagine when you "compile in your mind." With optimization, however, GCC will do a lot of things that it normally would not do, such as not bothering to emit "correct" instructions when faced with incorrect source code. There's literally no need for it to do anything at all when you say
p->
as your program's first act--it could just pretendmain()
was empty (or crash, as in your case).