2

我是 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怎么能被摧毁两次?

4

2 回答 2

6

如果您确保输出已刷新(std::endl例如,通过使用),您将获得

创建一个

创建 B

摧毁一个

内部范围结束

外部范围结束

破坏 B

摧毁一个

双重删除的原因A是您在shared_ptr这里从原始指针构造 a :

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

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

std::shared_ptr<A>{objectB};

那么你就不会遇到这个问题了。注意这里不需要 a static_cast

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

于 2013-07-08T06:46:02.220 回答
1

以防万一从 juanchopanza 的回答中不清楚,这条线

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

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

您在此处构造的共享指针只接受您给它的指针。它不知道该指针指向的对象已经被另一个智能指针拥有。此外,它不知道指针指向一个子对象,并且指针需要被转换回B*才能成为deleted,因为它static_cast隐藏了该信息。

要转换智能指针,您需要一个能够理解并与智能指针集成的转换。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";
}

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

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>函数接收到 aB*并且知道足够的信息来存储这样一个事实,即当删除发生时,它需要将A*它持有的 转换为B*。这可确保对象被正确删除。

然而,如果你真的想要你的类型A并且B表现出多态性,那么你可能应该通过添加虚拟析构函数来使它们成为多态类型,然后你不必担心这有多聪明std::shared_ptr

于 2013-07-08T15:47:59.710 回答