游戏后端技术_智能指针
最近更新:2024-09-23
|
字数总计:1.2k
|
阅读估时:4分钟
|
阅读量:次
- 智能指针
- 智能指针介绍
- 智能指针在TC中的应用
智能指针
智能指针介绍
- 指针管理的困境
- 资源释放了,指针没有置空
- 没有释放资源,产生内存泄漏
- 重复释放资源,引发coredump
- 指针未初始化
- 指向没有分配的空间
- 指向已经分配的空间
- 指向内核空间
- 即野指针的情况
- 野指针和悬挂指针
- 野指针是由于指针未被正确初始化或赋值导致的(未被初始化)
- 悬挂指针是由于指针指向的内存释放或重新分配导致的(曾经指向有效地址)
- 怎么解决的?
- 智能指针采用了RAII思想来自动化管理指针所指向的动态资源的释放
- RAII主要采用了对象的生命周期来控制程序资源
- 智能指针利用类的构造函数和析构函数来管理资源
- 智能指针的种类
- shared_ptr
- 语义:共享所有权
- 资源没有明确的拥有者
- 原理
- 场景
- 使用规范
- 使用shared_ptr来管理动态资源的时候,不要使用裸指针
- 构造智能指针的时候,不要暴露裸指针
- 尽量使用make_shared来构造智能指针
- 不要通过get来操作裸指针
- 不要用一个指针构造多个智能指针
- 不要用类对象指针(this)作为shared_ptr返回
- 需要记住:
1 2 3 4 5 6
| class T : public enable_shared_from_this<T>{ public: shared_ptr<T> self(){ return shared_from_this(); } };
|
- weak_ptr
- 辅助shared_ptr,用来解决shared_ptr循环引用,原因是弱引用不占用强引用计数
- unique_ptr
- 语义:没有拷贝构造,没有赋值运算操作符,仅提供了移动构造和移动赋值
- 明确某个对象只有一个拥有者
- 场景
- 使用规范
- 不支持拷贝,但是可以从一个函数返回一个unique_ptr
- 编译器优化
- 如果关闭编译器优化
- 有移动构造,调用移动构造
- 有拷贝构造,调用拷贝构造
- 没有拷贝构造,报错
- make_unique但是C++14
智能指针在TC中的应用
- 在连接池中的应用
- 接口的返回值是共享指针,因为接口内部分配了堆上的资源
- 接口使用是一次性的,使用完返回值后,内部堆上分配的空间能得到正确的释放
- 在网络模块中的应用
- socket类内部调用异步读操作时,需要调用自己的函数
- socket类使用方式都是采用智能指针方式
- 1.中需要socket类内部返回自己的智能指针
- 继承std::enable_shared_from_this
- 通过this->shared_from_this()返回自己的智能指针
- 在日志模块中的应用
- 如果采用异步刷日志的方式,为了让具体日志的生命周期得到延展,能在其他线程得到正常处理
- 把具体msg生成一个共享指针,通过lambda表达式捕获共享指针对象(引用计数为2)
- 未来在acceptor线程正确刷盘后,msg能得到正确释放
- 总之,在异步编程中,为了延展对象的生命周期,可以使用共享指针
- AI模块的应用
- TaskScheduler的实现
- 实现了管理下一帧执行的异步任务以及定时任务
- 定时器使用了multiset组织延时任务
- 定时器更新复用游戏帧更新update
- TaskScheduler的使用
- 案例1:每隔1s执行一下这个闭包任务
1 2 3 4 5 6 7 8 9 10
| void JustEnteredCombat(Unit* who) override { ScriptedAI::JustEnteredCombat(who); _scheduler.Schedule(1s, TASK_GROUP_COMBAT, [this](TaskContext aggro8YD) { if(Unit* victim = me->GetVictim()) DoCast(victim, SPELL_AGGRO_8_YD_PBAE, true); aggro8YD.Repeat(); }); }
|
- 案例2:2s后执行A,A执行时抛出一个3s后执行B的任务,B执行的时候抛出一个8s后执行C的任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Scheduler.Schedule(Seconds(2),[this](TaskContext task) { if(Creature* moragg = GetCreature(DATA_MORAGG)) { moragg->PlayDirectSound(SOUND_MORAGG_SPAWN); moragg->CastSpell(moragg, SPELL_MORAGG_EMOTE_ROAR); } task.Schedule(Seconds(3),[this](TaskContext task) { if(Creature* moragg = GetCreature(DATA_MORAGG)) moragg->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, MoraggPath, MoraggPathSize, true); task.Schedule(Seconds(8),[this](TaskContext ) { if(Creature* moragg = GetCreature(DATA_MORAGG)) { moragg->SetImmuneToAll(false); moragg->AI()->DoZoneInCombat(moragg); } }); }); });
|
- TaskScheduler和TaskContext
- 为什么需要TaskContext?需要记录任务,便于重新执行
- 类所属关系
- TaskScheduler由AI类管理,有可能会被AI释放,此时TaskContext假如又添加任务,是如何知道TaskScheduler是否存在的呢?
- TaskScheduler执行定时任务,需要构造TaskContext,TaskContext中使用weak_ptr来指向TaskScheduler
- 使用self_reference来检测TaskScheduler是否存在
1 2 3 4 5 6
| class TaskScheduler{ public: TaskScheduler(...):self_reference(this,[](TaskScheduler const*){}){} private: std::shared_ptr<TaskScheduler> self_reference; };
|
- TaskContext调用Repeat接口时,Dispatch将任务重新插入到TaskScheduler中
- 在TaskContext::Dispatch接口中,判断TaskScheduler是否还存在(weak_ptr.lock是否返回空?)
- 总之,利用shared_ptr和weak_ptr来实现对对象是否存在进行检测
- 利用shared_ptrsome(nullptr,{/这里清理资源/})
- some对象生命周期结束,会自动清理资源
2024-07-11
该篇文章被 Cleofwine
归为分类:
Game