问题描述
#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
,以便我可以访问派生类的方法?
谢谢!
解决方法
直接的解决方法是从 highs_list = np.array([])
list_length = 50
for current in range(1,len(df.index)):
previous = current - 1
if len(highs_list) < list_length:
np.append(highs_list,df.loc[current,'high'])
else:
np.roll(highs_list,-1)
highs_list[49] = df.loc[current,'high']
返回一个 shared_ptr:
getMsg
并停止在智能指针和原始指针之间进行不必要的转换。
消息对象必须保持活动状态,直到调用者完成使用它。由于您使用 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
最终转向代码审查,我故意省略了上面的 class MsgType {
public:
virtual ~MsgType() {}
virtual string getData() const = 0;
};
。
有一个类静态方法来访问全局队列很奇怪。如果您想保持这种布局,getMsg
队列可能也应该是静态类。
相反,最好在您真正需要的地方保留一个 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;
}
}
当 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>());