I took the code from this question and edited it to produce a segfault by explicitly calling the destructor of one of the move-constructed objects:
using namespace std;
struct Foo
{
Foo()
{
s = new char[100];
cout << "Constructor called!" << endl;
}
Foo(const Foo& f) = delete;
Foo(Foo&& f) :
s{f.s}
{
cout << "Move ctor called!" << endl;
f.s = nullptr;
}
~Foo()
{
cout << "Destructor called!" << endl;
cout << "s null? " << (s == nullptr) << endl;
delete[] s; // okay if s is NULL
}
char* s;
};
void work(Foo&& f2)
{
cout << "About to create f3..." << endl;
Foo f3(move(f2));
// f3.~Foo();
}
int main()
{
Foo f1;
work(move(f1));
}
Compiling and running this code (with G++ 4.9) produces the following output:
Constructor called!
About to create f3...
Move ctor called!
Destructor called!
s null? 0
Destructor called!
s null? 0
*** glibc detected *** ./a.out: double free or corruption (!prev): 0x0916a060 ***
Note that when the destructor is not explicitly called, no double-free error occurs.
Now, when I change the type of s
to unique_ptr<char[]>
and remove the delete[] s
in ~Foo()
and f.s = nullptr
in Foo(Foo&&)
, I do not get a double-free error:
Constructor called!
About to create f3...
Move ctor called!
Destructor called!
s null? 0
Destructor called!
s null? 1
Destructor called!
s null? 1
What is going on here? Why can the moved-to object be explicitly deleted when its data member is a unique_ptr
, but not when the invalidation of the moved-from object is handled manually in Foo(Foo&&)
? Since the move-constructor is called when f3
is created (as shown by the "Move ctor called!" line), why does the first destructor call (presumably for f3
) state that s
is not null? If the answer is simply that f3
and f2
are somehow actually the same object due to an optimization, what is unique_ptr
doing that's preventing the same problem from happening with that implementation?
Aucun commentaire:
Enregistrer un commentaire