Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

To use shared_ptr, is it safe ?

Tags:

c++

shared-ptr

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 ?

like image 755
ddh Avatar asked Nov 16 '10 15:11

ddh


2 Answers

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."

like image 149
James McNellis Avatar answered Oct 16 '22 04:10

James McNellis


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.

like image 32
Steve Jessop Avatar answered Oct 16 '22 05:10

Steve Jessop