简体   繁体   English

C++ - 抽象类的 shared_ptr

[英]C++ - shared_ptr of abstract class

I have the following code:我有以下代码:

#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;
}

In the MsgType::getMsg() method I can see the output, but in the main() I can't.MsgType::getMsg()方法中我可以看到输出,但在main()我看不到。 I belive that it's trying to call MsgType::getData() which is virtual.我相信它正在尝试调用虚拟的MsgType::getData()
How can I get the MsgType outside of this method, in a way that I can access the derived class' methods?如何在此方法之外获取MsgType ,以便我可以访问派生类的方法?

Thanks!谢谢!

The immediate fix is to just return a shared_ptr from getMsg :直接的解决方法是从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;
}

and stop converting needlessly between smart and raw pointers.并停止在智能指针和原始指针之间进行不必要的转换。

The message object must be kept alive until the caller has finished using it.消息对象必须保持活动状态,直到调用者完成使用它。 Since you're using shared_ptr to manage the object lifetime, you need a shared_ptr to continue existing as long as you want to use the object.由于您使用shared_ptr来管理对象生命周期,因此只要您想使用该对象,就需要一个shared_ptr来继续存在。

In general, mixing raw and smart pointers to the same objects is risky, because the smart pointers can only track the references they know about: that is, shared_ptr has to know everywhere a pointer to the object is being shared.一般来说,将原始指针和智能指针混合到相同的对象是有风险的,因为智能指针只能跟踪它们所知道的引用:也就是说, shared_ptr必须知道所有指向对象的指针被共享的地方。 It can only do this if every one of those pointers is a shared_ptr .只有当这些指针的每一个都是shared_ptr它才能做到这一点。


Note also that the easy way to diagnose object lifetime problems is to write a destructor that logs something.另请注意,诊断对象生命周期问题的简单方法是编写一个记录某些内容的析构函数。 This brings us on to the second problem: in order for MsgType to be a suitable abstract base class here, it needs a virtual destructor.这给我们带来了第二个问题:为了让MsgType在这里成为合适的抽象基类,它需要一个虚拟析构函数。

Without that, the shared_ptr will try to destroy your object when the refcount becomes zero, but be unable (in general) to do so correctly.没有它, shared_ptr将在 refcount 变为零时尝试销毁您的对象,但无法(通常)正确执行此操作。

class MsgType {
public:
    virtual ~MsgType() {}
    virtual string getData() const = 0;
};

Veering finally into code review, I intentionally omitted getMsg above.最后转向代码审查,我故意省略getMsg上面的getMsg

Having a class static method to access a global queue is just weird.使用类静态方法来访问全局队列很奇怪。 If you want to keep that layout, the allMsgs queue should probably be class static as well.如果你想保持这种布局, allMsgs队列也应该是静态类。

Instead, it's probably better to just keep a msg_queue object wherever you actually need it, with no statics or globals.相反,最好在您真正需要的地方保留一个msg_queue对象,没有静态或全局变量。

Here:这里:

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;
    }
}

When allMsgs is not empty you you copy front then pop .allMsgs不为空时,您复制front然后pop At that moment there is a single shared_ptr managing that object: msg_sp .那时有一个shared_ptr管理该对象: msg_sp Then you retrieve a raw pointer via get and return that, but when the function returns the use-count decrements to 0 and the managed object is destroyed.然后你通过get检索一个原始指针并返回它,但是当函数返回时使用计数递减为0并且托管对象被销毁。 The returned pointer is invalid.返回的指针无效。

I find your mixing of raw and shared_ptr a little confusing.我发现你对 raw 和shared_ptr的混合有点混乱。 When you have a shared_ptr managing the lifetime of the object then you cannot first get a raw pointer, then let the shared_ptr destroy the managed object and still use the raw pointer.当你有一个shared_ptr管理对象的生命周期时,你不能先获得一个原始指针,然后让shared_ptr销毁托管对象并仍然使用原始指针。 You need to properly transfer ownership when you don't want the shared pointer to destroy the managed object.当您不希望共享指针破坏托管对象时,您需要正确转移所有权。

I don't know why you are mixing std::shared_ptr and C-style pointers this way, but let's ignore this and assume it's just as an exercise.我不知道你为什么以这种方式混合std::shared_ptr和 C 风格的指针,但让我们忽略这一点并假设它只是作为一个练习。

Having a look at the bottom half of your code (slightly reduced), we have this:看看你的代码的下半部分(略有减少),我们有这个:

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 :) 

As as small addition, you can construct a shared base class pointer directly from a derived one, eg作为小的添加,您可以直接从派生的指针构造共享基类指针,例如

auto msg_sp = std::shared_ptr<MsgType>(std::make_shared<Msg1>());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM