I have got some confused about shared_ptr.
Say, I have classes:
class foo {
int _f;
};
typedef std::shared_ptr<foo> fooptr;
class bar {
int _b;
};
typedef std::shared_ptr<bar> barptr;
class foobar : public foo, public bar {
int _fb;
};
int main () {
foobar *fb1 = new foobar();
foobar *fb2 = new foobar();
fooptr f((foo *)fb1);
barptr b((bar *)fb2);
开发者_开发问答return 0;
}
Because b.get() != fb2, so it should crash when the program exit? Or it is safe ?
A shared_ptr<base>
can safely take ownership of a derived*
, even if base
does not have a virtual destructor.
However, this only works if shared_ptr
knows what the most derived type of the object is when it takes ownership of it. If you were to remove the casts
fooptr f(fb1);
fooptr b(fb2);
then you'd definitely be okay. With the casts, the shared_ptr
cannot know what the most-derived type of the object is when it takes ownership of it, so the behavior is undefined, just as if you had said:
foo* f = new foobar();
delete f;
The best thing to do is to follow the rule that "a base class destructor should be either public and virtual, or protected and nonvirtual."
No, it's not safe. foo
and bar
need virtual destructors, otherwise it's undefined what happens when the destructor of shared_ptr
deletes the pointer it's holding. Of course, saying that it's not safe isn't the same as saying it should crash.
Alternatively, there's some magic[*] built into shared_ptr
which means that if you don't cast to foo*
, your code becomes safe:
fooptr f(fb1);
barptr b(fb2);
Either with the virtual destructor, or if you take out the casts, when shared_ptr comes to delete the pointer, the compiler will "know" how to adjust the pointer back to its original type, in order to call the correct destructors and free the memory.
The magic only works because fb1
is type foobar*
, though. Without the virtual destructor, the following is still unsafe:
foo *fb = new foobar();
fooptr f(fb);
If you use shared_ptr
like this, then there's no risk of doing that:
fooptr f(new foobar());
You can also avoid the problem that in your code, that if the second call to new foobar()
throws an exception, the first object is leaked. If you're going to use shared_ptr
to manage memory for you, then you need to get the memory under management as quickly as possible:
fooptr f(new foobar());
barptr b(new foobar());
Now if the second line throws, f
will be properly destructed and will delete the object.
[*] "magic" = a constructor template, which stores in the shared_ptr
a pointer to a function which will cast the stored pointer back to the correct type, and then delete it.
foo
and bar
aren't polymorphic classes and therefore this code will most likely result in invalid pointer deallocation.
It is not safe since you are using a C-Style cast in a C++ code.
Do NOT use C-Style cast, use casts such as static_cast
, dynamic_cast
, const_cast
or reinterpret_cast
.
Plus, no crash does not mean safety.
In fact, just remove the casts in this case.
精彩评论