[英]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.