[英]C++ - shared_ptr of abstract class
我有以下代码:
#include <string>
#include <queue>
#include <thread>
#include <iostream>
using namespace std;
class MsgType {
public:
virtual string getData() const = 0;
static MsgType* getMsg();
};
class Msg1 : public MsgType {
string getData() const override final {
return "Msg1";
}
};
class Msg2 : public MsgType {
string getData() const override final {
return "Msg2";
}
};
queue<shared_ptr<MsgType>> allMsgs;
MsgType* MsgType::getMsg() {
shared_ptr<MsgType> msg_sp = nullptr;
if (!allMsgs.empty()) {
msg_sp = allMsgs.front();
allMsgs.pop();
}
if (msg_sp) {
MsgType* mt = msg_sp.get();
cout << "[in the method] " << mt->getData() << endl;
return mt;
} else {
return nullptr;
}
}
int main() {
MsgType* msg1 = new Msg1();
MsgType* msg2 = new Msg2();
shared_ptr<MsgType> msg;
msg.reset(msg1);
allMsgs.push(msg);
msg.reset(msg2);
allMsgs.push(msg);
MsgType* tryGetMsg = MsgType::getMsg();
cout << "[out of the method] " << tryGetMsg->getData() << endl;
}
在MsgType::getMsg()
方法中我可以看到输出,但在main()
我看不到。 我相信它正在尝试调用虚拟的MsgType::getData()
。
如何在此方法之外获取MsgType
,以便我可以访问派生类的方法?
谢谢!
直接的解决方法是从getMsg
返回一个 shared_ptr :
shared_ptr<MsgType> MsgType::getMsg() {
shared_ptr<MsgType> msg_sp;
if (!allMsgs.empty()) {
msg_sp = allMsgs.front();
allMsgs.pop();
}
if (msg_sp) {
cout << "[in the method] " << msg_sp->getData() << endl;
}
return msg_sp;
}
并停止在智能指针和原始指针之间进行不必要的转换。
消息对象必须保持活动状态,直到调用者完成使用它。 由于您使用shared_ptr
来管理对象生命周期,因此只要您想使用该对象,就需要一个shared_ptr
来继续存在。
一般来说,将原始指针和智能指针混合到相同的对象是有风险的,因为智能指针只能跟踪它们所知道的引用:也就是说, shared_ptr
必须知道所有指向对象的指针被共享的地方。 只有当这些指针中的每一个都是shared_ptr
它才能做到这一点。
另请注意,诊断对象生命周期问题的简单方法是编写一个记录某些内容的析构函数。 这给我们带来了第二个问题:为了让MsgType
在这里成为合适的抽象基类,它需要一个虚拟析构函数。
没有它, shared_ptr
将在 refcount 变为零时尝试销毁您的对象,但无法(通常)正确执行此操作。
class MsgType {
public:
virtual ~MsgType() {}
virtual string getData() const = 0;
};
最后转向代码审查,我故意省略getMsg
上面的getMsg
。
使用类静态方法来访问全局队列很奇怪。 如果你想保持这种布局, allMsgs
队列也应该是静态类。
相反,最好在您真正需要的地方保留一个msg_queue
对象,没有静态或全局变量。
这里:
MsgType* MsgType::getMsg() {
shared_ptr<MsgType> msg_sp = nullptr;
if (allMsgs.empty()) {
msg_sp = allMsgs.front();
allMsgs.pop();
}
if (msg_sp) {
MsgType* mt = msg_sp.get();
cout << "[in the method] " << mt->getData() << endl;
return mt;
} else {
return nullptr;
}
}
当allMsgs
不为空时,您复制front
然后pop
。 那时有一个shared_ptr
管理该对象: msg_sp
。 然后你通过get
检索一个原始指针并返回它,但是当函数返回时使用计数递减为0
并且托管对象被销毁。 返回的指针无效。
我发现你对 raw 和shared_ptr
的混合有点混乱。 当你有一个shared_ptr
管理对象的生命周期时,你不能先获得一个原始指针,然后让shared_ptr
销毁托管对象并仍然使用原始指针。 当您不希望共享指针破坏托管对象时,您需要正确转移所有权。
我不知道你为什么以这种方式混合std::shared_ptr
和 C 风格的指针,但让我们忽略这一点并假设它只是作为一个练习。
看看你的代码的下半部分(略有减少),我们有这个:
std::queue<std::shared_ptr<MsgType>> allMsgs;
MsgType* MsgType::getMsg();
int main() {
MsgType* msg1 = new Msg1();
std::shared_ptr<MsgType> msg;
msg.reset(msg1); // <--- 1. here, msg1 is owned by msg
allMsgs.push(msg); // <--- 2. now, msg1 is also owned by allMsgs
msg.reset(); // <--- 3. msg1 only owned by allMsgs
MsgType* tryGetMsg = MsgType::getMsg(); // <--- see below : nobody keeping msg1 alive!
std::cout << "[out of the method] " << tryGetMsg->getData() << std::endl;
}
MsgType* MsgType::getMsg() {
std::shared_ptr<MsgType> msg_sp = nullptr;
if (!allMsgs.empty()) {
msg_sp = allMsgs.front(); // <--- 4. msg1 owned by msg_sp & allMsgs
allMsgs.pop(); // <--- 5. msg1 owned by msg_sp only
}
if (msg_sp) {
MsgType* mt = msg_sp.get();
std::cout << "[in the method] " << mt->getData() << std::endl;
return mt;
} else {
return nullptr;
}
} // <--- 6. msg_sp destroyed... oh oh... msg1 dead :)
作为小的添加,您可以直接从派生的指针构造共享基类指针,例如
auto msg_sp = std::shared_ptr<MsgType>(std::make_shared<Msg1>());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.