简体   繁体   English

如何使用静态强制转换来管理共享对象的生命周期?

[英]How to manage shared object lifetimes with static casts?

I am new to C++ style casts, and need help in understanding how the code below works (this is some dummy code I wrote to understand things). 我是C ++样式转换的新手,并且需要帮助来理解下面的代码如何工作(这是我编写的一些了解情况的伪代码)。

#include <iostream>
#include <memory>

class A { 
public:
    A() : a(1) {
        std::cout << "Creating A\n";
    }   
    ~A() {
        std::cout << "Destroying A\n";
    }   
    int a;
};

class B : public A { 
public:
    B() : b(2) {
        std::cout << "Creating B\n";
    }   
    ~B() {
        std::cout << "Destroying B\n";
    }   
    int b;
};

int main() {
    std::shared_ptr<B> objectB(new B());
    {   
    std::shared_ptr<A> (static_cast<A*>(objectB.get()));
    std::cout << "End of inner scope\n";
    }   
    std::cout << "End of outer scope\n";
}

It prints 它打印

Creating A
Creating B
Destroying A
End of inner scope
End of outer scope
Destroying B
Destroying A

My understanding: 我的理解:

Creating A         -> B's ctor calls base class ctor
Creating B         -> B's ctor
Destroying A       -> ???
End of inner scope
End of outer scope
Destroying B       -> B's dtor
Destroying A       -> B's dtor calls base dtor

Why do I get the first Destroying A and what exactly is happening here?! 为什么我会得到第一个Destroying A以及这里到底发生了什么?! How can A be destroyed twice? 如何将A销毁两次?

If you make sure the output is flushed (by using std::endl , for example), you get 如果您确保输出已刷新(例如,使用std::endl ),则会得到

Creating A 创造A.

Creating B 创造B.

Destroying A 摧毁A.

End of inner scope 内部范围结束

End of outer scope 外部范围结束

Destroying B 摧毁B.

Destroying A 摧毁A.

the reason for the double delete of A is that you are constructing a shared_ptr from a raw pointer here: 两次删除A的原因是您要从此处的原始指针构造一个shared_ptr

std::shared_ptr<A> (static_cast<A*>(objectB.get()));

This shared_ptr is completely independent of the first one, and has its own reference count. 这个shared_ptr完全独立于第一个,并且有自己的引用计数。 So when the scope ends, it attempts to delete the A pointer it holds. 因此,当范围结束时,它会尝试删除它所持有的A指针。 If you had done this instead: 如果您这样做,则:

std::shared_ptr<A>{objectB};

then you wouldn't have encountered the problem. 那么您就不会遇到问题。 Note there is no need for a static_cast here. 请注意,此处不需要static_cast

Note that A should have a virtual destructor. 请注意, A应该具有virtual析构函数。 shared_ptr has a clever destruction mechanism which means that this is not crucial in this example, but in general, if you are going to delete objects polymorphically, the base class must have a virtual destructor. shared_ptr具有聪明的销毁机制,这意味着在此示例中这并不重要,但是通常,如果要多态删除对象,则基类必须具有虚拟析构函数。

Just in case it wasn't clear from juanchopanza's answer, this line 如果从juanchopanza的回答中不清楚这条线

std::shared_ptr<A> (static_cast<A*>(objectB.get()));

is incorrect and leads to undefined behavior. 是不正确的,导致未定义的行为。

The shared pointer you construct here just takes the pointer you give it. 您在此处构造的共享指针仅使用您提供的指针。 It has no idea that the object pointed to by that pointer is already owned by another smart pointer. 不知道该指针指向的对象已被另一个智能指针拥有。 Furthermore it doesn't know that the pointer points to a sub-object and that the pointer will need to be cast back to B* in order to be delete d, because the static_cast hides that information from it. 此外,它不知道指针指向一个子对象,并且该指针将需要被static_castB*才能delete d,因为static_cast会将信息隐藏在其中。

To cast smart pointers you need a cast that understands and is integrated with smart pointers. 要投射智能指针,您需要一个能够理解并与智能指针集成在一起的演员表。 C++ has std::static_pointer_cast and std::dynamic_pointer_cast for this purpose. 为此,C ++具有std::static_pointer_caststd::dynamic_pointer_cast

int main() {
  std::shared_ptr<B> objectB(new B());
  {   
    std::shared_ptr<A> x = std::static_pointer_cast<A>(objectB);
    std::cout << "End of inner scope\n";
  }   
  std::cout << "End of outer scope\n";
}

With this code the output of your program shows correct behavior: 使用此代码,程序的输出将显示正确的行为:

make A
make B
End of inner scope
End of outer scope
~B
~A

Of course in this particular case you don't need an explicit cast, because std::shared_ptr can figure out legal up-casts and do them implicitly. 当然,在这种特殊情况下,您不需要显式转换,因为std::shared_ptr可以找出合法的转换并隐式执行。 You need std::static_pointer_cast to do down-casts though: 你需要std::static_pointer_cast进行向下转换:

int main() {
  std::shared_ptr<A> objectA(new B());
  {   
    std::shared_ptr<B> x = std::static_pointer_cast<B>(objectA);
    std::cout << "End of inner scope\n";
  }   
  std::cout << "End of outer scope\n";
}

Thanks, one last thing, if I don't need objectB, is it safe to do std::shared_ptr<A> objectA(new B()); 谢谢,最后一件事,如果我不需要objectB,可以安全地执行std::shared_ptr<A> objectA(new B()); ?

Yes, this does happen to be safe. 是的,这确实是安全的。 The shared_ptr<A> constructor receives a B* and knows enough to store the fact that when deletion occurs it will need to convert the A* that it holds to B* . shared_ptr<A>构造函数接收B*并且知道足以存储这样的事实:当发生删除时,它将需要将它保持的A*转换为B* This ensures that the object gets deleted correctly. 这可确保正确删除对象。

However, if you really want your types A and B to behave polymorphically then you should probably just make them polymorphic types by adding virtual destructors, and then you don't have to worry about how smart std::shared_ptr is about this. 但是,如果您确实希望类型A和类型B具有多态性,则应该通过添加虚拟析构函数使它们成为多态类型,然后不必担心std::shared_ptr多么聪明。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM