1 C++关于使用std::shared_ptrstd::weak_ptr在两个互有依赖关系的类中的设计

先说一下需求场景,比如我现在需要写一个图形学渲染器,一个渲染器中可能包含多个渲染场景Scene类,每个Scene类又包含了多个需要渲染的模型Actor类,现在我在每个Scene里面通过维护一个std::vector<std::shared_ptr<Actor>>的容器用于管理加载到当前Scene中的所有Actor,而每一个Actor类也需要维护一个指向加载其模型的Scene类,用于后续归属关系判断以及调用当前Scene类中的成员函数,比如获取当前场景Scene中所使用灯光等,那么我们该如何设计一个模式在保证内存安全的情况下实现上述需求呢?

1.1 极易出现问题的代码

如果对C++智能指针使用不熟悉,很可能会写出下面有问题的代码。在Scene类中使用std::vector<std::shared_ptr<Actor>>保存添加的Actor共享智能指针,然后在Actor类中同样使用std::shared_ptr<Scene>保存Scene共享智能指针,这种情况就是典型的循环引用问题,造成内存泄漏。

示例代码:

#include <iostream>
#include <memory>
#include <vector>

#include <vld.h>

class Actor;
class Scene
{
public:
    Scene()
    {

    }

    virtual~ Scene()
    {
        std::cout << "Scene destruct" << std::endl;
    }

    void AddActor(std::shared_ptr<Actor> p_actor)
    {
        m_pActorVec.emplace_back(p_actor);
    }

    std::shared_ptr<Actor> GetActor(int index)
    {
        return m_pActorVec[index];
    }

    int GetActorNum()
    {
        return m_pActorVec.size();
    }

private:
    std::vector<std::shared_ptr<Actor>> m_pActorVec;
};

class Actor
{
public:
    Actor()
    {

    }

    Actor(std::shared_ptr<Scene> p_scene)
    {
        m_pScene = p_scene;
    }

    virtual~Actor()
    {
        std::cout << "Actor destruct" << std::endl;
    }

    std::shared_ptr<Scene> GetScene()
    {
        return m_pScene;
    }


private:
    std::shared_ptr<Scene> m_pScene;
};


int main()
{
    std::shared_ptr<Scene> p_scene = std::make_shared<Scene>();

    for (int i = 0; i < 10; i++)
    {
        std::shared_ptr<Actor> p_actor = std::make_shared<Actor>(p_scene);
        p_scene->AddActor(p_actor);
    }

    std::cout << "p_scene.use_count = " << p_scene.use_count() << std::endl;

    if (p_scene->GetActor(0)->GetScene() != nullptr)
    {
        auto temp_scene = p_scene->GetActor(0)->GetScene();
        if (temp_scene != nullptr)
        {
            std::cout <<"ActorNum = " << temp_scene->GetActorNum() << std::endl;
        }
    }

    return 0;
}

1.2 循环引用问题修改

1.1节中出现的循环引用问题,我们可以通过在Actor类中使用std::weak_ptr<Scene>保存Scene的共享智能指针进行解决。

在这里需要注意的是,由于在Actor类中使用std::weak_ptr<Scene>保存Scene的共享智能指针,如果我们直接使用std::weak_ptr型的智能指针调用Scene中的成员方法是没有办法成功的,这个使用需要使用std::weak_ptrlock()方法获取Scene中被管理的Shared_ptr对象,然后再调用Scene类中的成员方法。

详细的改动如下:

#include <iostream>
#include <memory>
#include <vector>

class Actor;
class Scene
{
public:
    Scene()
    {

    }

    virtual~ Scene()
    {
        std::cout << "Scene destruct" << std::endl;
    }

    void AddActor(std::shared_ptr<Actor> p_actor)
    {
        m_pActorVec.emplace_back(p_actor);
    }

    std::shared_ptr<Actor> GetActor(int index)
    {
        return m_pActorVec[index];
    }

    int GetActorNum()
    {
        return m_pActorVec.size();
    }

private:
    std::vector<std::shared_ptr<Actor>> m_pActorVec;
};

class Actor
{
public:
    Actor()
    {

    }

    Actor(std::shared_ptr<Scene> p_scene)
    {
        m_pScene = p_scene;
    }

    virtual~Actor()
    {
        std::cout << "Actor destruct" << std::endl;
    }

    std::shared_ptr<Scene> GetScene()
    {
        if (m_pScene.expired())
            return nullptr;

        return m_pScene.lock();
    }


private:
    std::weak_ptr<Scene> m_pScene;
};


int main()
{
    std::shared_ptr<Scene> p_scene = std::make_shared<Scene>();

    for (int i = 0; i < 10; i++)
    {
        std::shared_ptr<Actor> p_actor = std::make_shared<Actor>(p_scene);
        p_scene->AddActor(p_actor);
    }

    std::cout << "p_scene.use_count = " << p_scene.use_count() << std::endl;

    if (p_scene->GetActor(0)->GetScene() != nullptr)
    {
        auto temp_scene = p_scene->GetActor(0)->GetScene();
        if (temp_scene != nullptr)
        {
            std::cout <<"ActorNum = " << temp_scene->GetActorNum() << std::endl;
        }
    }

    return 0;
}

参考链接