C++ 使用共享ptr安全吗?

C++ 使用共享ptr安全吗?,c++,shared-ptr,C++,Shared Ptr,我对共享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 *fb

我对共享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;
}
class-foo{
国际货币基金组织;
};
typedef std::shared_ptr fooptr;
分类栏{
int_b;
};
typedef std::shared_ptr barptr;
foobar类:公共foo,公共bar{
国际足联;
};
int main(){
foobar*fb1=新的foobar();
foobar*fb2=新foobar();
fooptrf((foo*)fb1);
barptr b((bar*)fb2);
返回0;
}

因为b.get()!=fb2,所以当程序退出时它应该崩溃?或者它是安全的?

foo
bar
不是多态类,因此此代码很可能导致无效的指针释放。

不,它不安全
foo
bar
需要虚拟析构函数,否则当
shared\u ptr
的析构函数删除它所持有的指针时会发生什么,这是未定义的。当然,说它不安全并不等于说它应该崩溃

或者,在
shared\u ptr
中内置了一些神奇的[*],这意味着如果您不使用
foo*
,您的代码将变得安全:

fooptr f(fb1);
barptr b(fb2);
无论是使用虚拟析构函数,还是删除强制类型转换,当shared_ptr删除指针时,编译器将“知道”如何将指针调整回其原始类型,以便调用正确的析构函数并释放内存

这种魔法之所以有效,是因为fb1的类型是foobar*。如果没有虚拟析构函数,以下情况仍然不安全:

foo *fb = new foobar();
fooptr f(fb);
如果您像这样使用
shared\u ptr
,则不存在这样做的风险:

fooptr f(new foobar());
您还可以避免在代码中出现这样的问题:如果对
new foobar()
的第二次调用引发异常,那么第一个对象就会泄漏。如果要使用
shared_ptr
为您管理内存,则需要尽快管理内存:

fooptr f(new foobar());
barptr b(new foobar());
现在,如果第二行抛出,
f
将被正确销毁并删除该对象

[*]“magic”=一个构造函数模板,它在
shared\u ptr
中存储一个指向函数的指针,该函数将把存储的指针转换回正确的类型,然后将其删除。

a
shared\u ptr
可以安全地拥有
派生的*
,即使
base
没有虚拟析构函数

但是,只有当
shared_ptr
知道对象的最派生类型是什么时,这才有效。如果你要移除石膏

fooptr f(fb1);
fooptr b(fb2);
那你肯定会没事的。使用强制转换时,
shared\u ptr
无法知道对象的最派生类型是什么,因此行为未定义,就像您说过的:

foo* f = new foobar();
delete f;

<> P>最好遵循以下规则:

< P>它不安全,因为你在C++代码中使用了C风格的铸件。 不要使用C样式强制转换,请使用强制转换,例如
静态强制转换
动态强制转换
常量强制转换
重新解释强制转换
。 此外,没有碰撞并不意味着安全


实际上,在这种情况下只需删除强制类型转换。

如果foo和bar都有虚拟析构函数,那么它是安全的?但是,去掉显式强制类型转换,它是安全的,因为
shared\u ptr
有一个模板化构造函数,可以通过最初传递给构造函数的指针类型确保销毁,不是最终导致最终销毁的
共享\u ptr
的模板参数。在基类中使用虚拟析构函数仍然是非常明智的;但这在技术上并不危险。我认为在shared_ptr的析构函数中,应该有“delete ptr”,delete do的操作:1)调用对象的析构函数,2)释放内存。但是如果内存allcoating是0x10000,但是共享的ptr得到的是0x100004,那么应该会有问题吗?@Charles:是的,我会找到的。一次做一件事;-)@史蒂夫:我只是想指出:)@doudehou:拥有一个虚拟析构函数是程序员用来告诉编译器插入它所需要的任何魔法,从而通过指向基类的指针安全地删除一个对象。如果程序员不这样做,编译器可以省略任何检查,并假设指向的对象是基类实例,而只有基类是实例。实际发生的情况因实现而异,因为没有任何保证(未定义的行为)。通常,会忽略一些析构函数调用,但也可能发生内存错误。
dynamic\u cast
拼写为
dynamic\u cast
,句点。