C++ – 在两个互有依赖关系的类中使用std::shared_ptr和std::weak_ptr进行内存管理
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:C++ – 在两个互有依赖关系的类中使用std::shared_ptr和std::weak_ptr进行内存管理
原文链接:https://www.stubbornhuang.com/2147/
发布于:2022年05月31日 17:08:46
修改于:2022年05月31日 17:24:30
1 C++关于使用std::shared_ptr和std::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_ptr
的lock()
方法获取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;
}
参考链接
当前分类随机文章推荐
- C++ - 数组初始化 阅读367次,点赞0次
- C++ - queue存储动态指针时正确释放内存 阅读5539次,点赞2次
- C++ - 从std::string的文件路径中获取文件夹、文件名、文件后缀名 阅读247次,点赞0次
- C++ - std::map - 存储动态指针时正确释放内存 阅读4251次,点赞1次
- C++ - 使用标准库实现事件和委托,信号和槽机制 阅读403次,点赞0次
- C++ - linux编译C++代码出现error: use of deleted function std::atomic
::atomic(const std::atomic 阅读2753次,点赞0次&) - C++11/std::atomic - 原子变量(不加锁实现线程互斥) 阅读6382次,点赞2次
- C++ - std::string与std::wstring相互转换 阅读1960次,点赞0次
- C++ - 动态链接库dll为什么要使用unsigned char作为byte的内部格式 阅读729次,点赞0次
- C++ - 使用Crypto++/CryptoPP加解密库对字符串或者文件进行AES256加密 阅读3513次,点赞1次
全站随机文章推荐
- ThreeJS - 如何提升three.js的渲染效果?看完这篇你可能会有启发 阅读3659次,点赞0次
- 书籍翻译 – Fundamentals of Computer Graphics, Fourth Edition,第7章 Viewing中文翻译 阅读3210次,点赞7次
- 资源分享 - Ray Tracing - The Rest of Your Life英文高清PDF下载 阅读2434次,点赞0次
- WordPress - 在用户登录页面添加自定义提示信息 阅读1626次,点赞0次
- 姿态估计之COCO数据集骨骼关节keypoint标注对应 阅读9969次,点赞5次
- WordPress - 修复Markdown编辑器插件WP-Editor.md在插入php代码块后代码中的$符号无法正常显示的问题 阅读1253次,点赞0次
- Numpy - 保存和加载numpy数组、字典、列表数据 阅读757次,点赞0次
- C++ - std::map正向遍历与反向遍历的几种方式 阅读4478次,点赞3次
- 资源分享 – OpenGL SuperBible - Comprehensive Tutorial and Reference (Sixth Edition) OpenGL蓝宝书第6版英文高清PDF下载 阅读1924次,点赞0次
- Modern OpenGL从零开始 - 从茫茫多的OpenGL第三方库讲起 阅读3551次,点赞1次
评论
169