我对shared_ptr感到困惑。
说,我有课程:
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;
}
因为b.get()!= fb2,所以当程序退出时它会崩溃吗?还是安全的?
答案 0 :(得分:10)
即使shared_ptr<base>
没有虚拟析构函数,derived*
也可以安全地获得base
的所有权。
但是,这仅在shared_ptr
知道对象的所有权最多时才知道该对象的所有权。如果您要删除演员表
fooptr f(fb1);
fooptr b(fb2);
那时你肯定没问题。使用强制类型转换时,shared_ptr
无法知道对象的最大派生类型是什么,因此行为是未定义的,就像您曾说过:
foo* f = new foobar();
delete f;
最好的办法是遵循"a base class destructor should be either public and virtual, or protected and nonvirtual."
的规则答案 1 :(得分:7)
不,这不安全。 foo
和bar
需要虚拟析构函数,否则未定义当shared_ptr
的析构函数删除它所持有的指针时会发生什么。当然,说它不安全与说它应该崩溃是不一样的。
或者,shared_ptr
中内置了一些魔法[*],这意味着如果你不转换为foo*
,你的代码就会变得安全:
fooptr f(fb1);
barptr b(fb2);
使用虚拟析构函数,或者如果取出强制转换,当shared_ptr删除指针时,编译器将“知道”如何将指针调整回其原始类型,以便调用正确的析构函数和释放记忆。
只有fb1
是foobar*
类型才能使用魔法。如果没有虚拟析构函数,以下内容仍然不安全:
foo *fb = new foobar();
fooptr f(fb);
如果你像这样使用shared_ptr
,那么就没有这样做的风险了:
fooptr f(new foobar());
您还可以避免代码中的问题,即如果第二次调用new foobar()
引发异常,则会泄漏第一个对象。如果您打算使用shared_ptr
为您管理内存,那么您需要尽快管理内存:
fooptr f(new foobar());
barptr b(new foobar());
现在,如果第二行抛出,f
将被正确销毁,并将删除该对象。
[*]“magic”=一个构造函数模板,它在shared_ptr
中存储一个指向函数的指针,该函数将存储的指针强制转换回正确的类型,然后将其删除。
答案 2 :(得分:0)
foo
和bar
不是多态类,因此这段代码很可能会导致无效的指针释放。
答案 3 :(得分:-1)
由于您在C ++代码中使用C样式转换,因此不安全。
请勿使用C风格演员表,使用static_cast
,dynamic_cast
,const_cast
或reinterpret_cast
等演员表。
此外,没有崩溃并不意味着安全。
事实上,在这种情况下只需删除演员表。