我是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怎么能被摧毁两次?
答案 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*
。这可以确保正确删除对象。
但是,如果您真的希望类型A
和B
具有多态性,那么您应该通过添加虚拟析构函数来使它们成为多态类型,然后您不必担心如何聪明的std::shared_ptr
就是这个。