如何组织 class 的对象所有权,其寿命比对象的所有者短?
How to organize object ownership for class that lives lesser time than owner of the object?
我有以下情况:有 class 个 GraphicsContext:
class GraphicsContext {
...
private:
std::unique_ptr<Renderer> m_renderer;
}
还有一个 class 的应用程序使用了 GraphicsContext:
class Application {
...
private:
std::unique_ptr<GraphicsContext> m_graphicsContext;
}
还有一些子级别 classes,它们在应用程序 class 中使用,并且使用来自 GraphicsContext 的渲染器。我需要在这些 classes 中存储指向渲染器的指针,但我应该怎么做?
class SubLevelClass {
public:
SubLevelClass(Renderer* renderer);
...
void drawSomething();
private:
Renderer* m_renderer; // this class is not owner of Renderer but should can ability to refer to it
}
这样的子级别 classes 在语义上不拥有渲染器,因此我认为使用 shared_ptr 而不是 unique_ptr 不是个好主意。但是,如果保证子级别 classes 的对象比 Application 对象存在的时间短,如何组织这种所有权呢?我可以从 GraphicsContext 中存储 return 一个指向 Renderer 的原始指针,或者这是语义错误的想法?
有一些方法可以解决。
这些代码未经测试,但应该足以展示这个想法。
解决方案 1:清楚地跟踪
解决方案 1A:-
class Renderer{
std::vector<SubLevelClass*> whoThatLinkBackToMe; //or std::unordered_set
public: ~Renderer(){
//assert that whoThatLinkBackToMe.size() == 0
}
};
class SubLevelClass{
SubLevelClass(Renderer* renderer){
renderer->whoThatLinkBackToMe.push_back(this);
}
//.. destructor should remove "this" from "renderer->whoThatLinkBackToMe" too
};
解决方案 1B :-
class CentralizeSystem{
public: static std::unordered_map<Renderer*,SubLevelClass*> map;
};
class Renderer{
public: ~Renderer(){
//assert that CentralizeSystem::map[this].size() == 0
}
};
class SubLevelClass{
SubLevelClass(Renderer* renderer){
CentralizeSystem::map.add(renderer,this);
}
//.. destructor should remove "this" from "CentralizeSystem::map" too
};
解决方案 2:实体组件系统 (ECS)
这是一场需要付出巨大努力的设计革命 :-
- 在 ECS 中创建
Renderer
系统。因此,最后自动删除。
- 使
SubLevelClass
成为 ECS 中的组件。尝试将所有信息(字段、缓存)存储在 SubLevelClass
- 而不是 Renderer
中。仅这两件事就可以解决您的问题。
但是,如果运气不好你需要让 Renderer
不是单例(成为组件):
3.1 创建一个新组件 Component_CheckDelete
例如:-
class Component_CheckDelete : public Component{
public: bool ToBeDeleted=false;
};
3.2 每当要删除Renderer
时,只需标记其Component_CheckDelete::ToBeDeleted=true
。
然后,在时间步结束时,检查 SubLevelClass
.
的每个实例
如果有一些 SubLevelClass
引用具有 convertToComponent<Component_CheckDelete>(rendererPtr)->ToBeDeleted==true
的 Renderer
,则抛出断言失败。
解决方案 3
忽略整个问题。
这是用户端的错误。引擎创建者不应该发现每个用户的错误。
Bullet Physics(最好的物理引擎之一)经常使用这种方法 - 如果我删除它的 boardphase 模块并仍然使用它的主引擎,我可能会遇到不负责任的访问冲突。
我的看法: 我一般选方案3,有时选方案2,很少选方案1A。
std::shared_ptr
是正确的解决方案,恕我直言。
Such sub-level classes does not semantically own the Renderer and therefore I think it't not good idea to use shared_ptr
如果子级别 类 需要延长 Renderer
对象的生命周期以匹配他们自己的对象,那么他们共享它的所有权。
我有以下情况:有 class 个 GraphicsContext:
class GraphicsContext {
...
private:
std::unique_ptr<Renderer> m_renderer;
}
还有一个 class 的应用程序使用了 GraphicsContext:
class Application {
...
private:
std::unique_ptr<GraphicsContext> m_graphicsContext;
}
还有一些子级别 classes,它们在应用程序 class 中使用,并且使用来自 GraphicsContext 的渲染器。我需要在这些 classes 中存储指向渲染器的指针,但我应该怎么做?
class SubLevelClass {
public:
SubLevelClass(Renderer* renderer);
...
void drawSomething();
private:
Renderer* m_renderer; // this class is not owner of Renderer but should can ability to refer to it
}
这样的子级别 classes 在语义上不拥有渲染器,因此我认为使用 shared_ptr 而不是 unique_ptr 不是个好主意。但是,如果保证子级别 classes 的对象比 Application 对象存在的时间短,如何组织这种所有权呢?我可以从 GraphicsContext 中存储 return 一个指向 Renderer 的原始指针,或者这是语义错误的想法?
有一些方法可以解决。
这些代码未经测试,但应该足以展示这个想法。
解决方案 1:清楚地跟踪
解决方案 1A:-
class Renderer{
std::vector<SubLevelClass*> whoThatLinkBackToMe; //or std::unordered_set
public: ~Renderer(){
//assert that whoThatLinkBackToMe.size() == 0
}
};
class SubLevelClass{
SubLevelClass(Renderer* renderer){
renderer->whoThatLinkBackToMe.push_back(this);
}
//.. destructor should remove "this" from "renderer->whoThatLinkBackToMe" too
};
解决方案 1B :-
class CentralizeSystem{
public: static std::unordered_map<Renderer*,SubLevelClass*> map;
};
class Renderer{
public: ~Renderer(){
//assert that CentralizeSystem::map[this].size() == 0
}
};
class SubLevelClass{
SubLevelClass(Renderer* renderer){
CentralizeSystem::map.add(renderer,this);
}
//.. destructor should remove "this" from "CentralizeSystem::map" too
};
解决方案 2:实体组件系统 (ECS)
这是一场需要付出巨大努力的设计革命 :-
- 在 ECS 中创建
Renderer
系统。因此,最后自动删除。 - 使
SubLevelClass
成为 ECS 中的组件。尝试将所有信息(字段、缓存)存储在SubLevelClass
- 而不是Renderer
中。仅这两件事就可以解决您的问题。 但是,如果运气不好你需要让
Renderer
不是单例(成为组件):3.1 创建一个新组件
Component_CheckDelete
例如:-class Component_CheckDelete : public Component{ public: bool ToBeDeleted=false; };
3.2 每当要删除
Renderer
时,只需标记其Component_CheckDelete::ToBeDeleted=true
。
然后,在时间步结束时,检查SubLevelClass
.
的每个实例 如果有一些SubLevelClass
引用具有convertToComponent<Component_CheckDelete>(rendererPtr)->ToBeDeleted==true
的Renderer
,则抛出断言失败。
解决方案 3
忽略整个问题。
这是用户端的错误。引擎创建者不应该发现每个用户的错误。
Bullet Physics(最好的物理引擎之一)经常使用这种方法 - 如果我删除它的 boardphase 模块并仍然使用它的主引擎,我可能会遇到不负责任的访问冲突。
我的看法: 我一般选方案3,有时选方案2,很少选方案1A。
std::shared_ptr
是正确的解决方案,恕我直言。
Such sub-level classes does not semantically own the Renderer and therefore I think it't not good idea to use shared_ptr
如果子级别 类 需要延长 Renderer
对象的生命周期以匹配他们自己的对象,那么他们共享它的所有权。