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

时间:2013-07-08 06:32:30

标签: c++ c++11 shared-ptr static-cast

我是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";
}

打印

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

我的理解:

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

为什么我会得到第一个Destroying A以及这里到底发生了什么?! A怎么能被摧毁两次?

2 个答案:

答案 0 :(得分:6)

如果您确保刷新输出(例如,使用std::endl),则

  

创建A

     

创建B

     

销毁A

     

内部范围结束

     

外部范围结束

     

摧毁B

     

销毁A

双重删除A的原因是您正在从原始指针构建shared_ptr

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

这个shared_ptr完全独立于第一个,并且有自己的引用计数。因此,当作用域结束时,它会尝试删除它所持有的A指针。如果你这样做了:

std::shared_ptr<A>{objectB};
那么你就不会遇到这个问题。请注意,这里不需要static_cast

请注意,A应该有一个virtual析构函数。 shared_ptr有一个聪明的破坏机制,这意味着这在这个例子中并不重要,但一般来说,如果要以多态方式删除对象,基类必须有一个虚拟析构函数。

答案 1 :(得分:1)

如果从juanchopanza的回答中不清楚这一行

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

不正确并导致未定义的行为。

你在这里构造的共享指针只接受你给它的指针。它不知道该指针指向的对象已经被另一个智能指针所拥有。此外,它不知道指针指向子对象,并且指针需要被转换回B*以便delete d,因为static_cast隐藏来自它的信息。

要投射智能指针,您需要一个能够理解并与智能指针集成的强制转换。为此,C ++有 std::static_pointer_cast std::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";
}

使用此代码,程序的输出显示正确的行为:

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

当然,在这种特殊情况下,您不需要显式转换,因为std::shared_ptr可以找出合法的转换并隐式执行。您需要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";
}
  

谢谢,最后一件事,如果我不需要objectB,那么std::shared_ptr<A> objectA(new B());安全吗?

是的,这确实是安全的。 shared_ptr<A>构造函数接收B*并且知道足以存储以下事实:当发生删除时,它需要将其保留的A*转换为B*。这可以确保正确删除对象。

但是,如果您真的希望类型AB具有多态性,那么您应该通过添加虚拟析构函数来使它们成为多态类型,然后您不必担心如何聪明的std::shared_ptr就是这个。