在多个 objects 中使用地图
Use a map across multiple objects
我使用全球地图来注册一个或多个相同类型的 object。为此,我开始使用全局命名空间。
以此为例(代码未经测试,仅作为示例):
//frame.h
class frame
{
public:
frame(int id);
~frame();
};
namespace frame_globals
{
extern std::map<int, frame *> global_frameMap;
};
//frame.cpp
#include "frame.h"
namespace frame_globals
{
std::map<int, frame *> global_frameMap;
}
frame::frame(int id)
{
//[...]
//assuming this class is exclusively used with "new"!
frame_globals::global_frameMap[id] = this;
}
frame::~frame()
{
frame_globals::global_frameMap.erase(id);
}
这是我使用的一个相当快速的解决方案,但现在我再次偶然发现了用例,我需要注册我的 objects,我问自己是否没有比使用全局变量(我想摆脱它)。
[编辑:似乎不正确]
静态成员变量(据我所知)没有选择,因为静态在每个 object 中都是内联的,我在所有 object 中都需要它。
最好的方法是什么?想到使用常见的 parent,但我想轻松访问我的框架 class。你会怎么解决?
或者您是否知道注册 object 的更好方法,以便我可以从其他地方获得指向它的指针?所以我可能不会完全需要 "new" 并保存 "this"?
我会考虑逆向设计。例如。使用语言内置功能有什么问题?
std::map<int, frame> frames_;
frames_.emplace(id);//adding, ie. creating frames
frames_.erase(id)//removing, ie. deleting frames
现在 creating/deleting 和以前一样简单。巧合的是,如果有人出于不同目的需要框架,这里没有问题。如果 frame 应该是多态的,你可以存储 std::unique_ptr
而不是。
另外,我会考虑让 id
成为 frame 的成员,然后存储在 std::set
而不是 std::map。
class frame
{
public:
int id;
frame(int id);
friend bool operator <(const frame& lhs, const frame& rhs) {return lhs.id < rhs.id;}
};
std::set<frame> frames_;
删除只是:
frames_.erase(frame);
然而,为了根据 id 查找现在有点复杂。幸运的是手边有一个解决方案。这需要实现一个透明的比较,其中包括定义 is_transparent
.
总体而言,您需要考虑所有权。问问自己:在哪里或谁会 own/store map/frame pointers/etc。把它存放在那里。在共享 所有权 的情况下,使用 shared_ptr
(但仅在实际需要时使用此模式 - 这比人们使用它的情况更罕见)。
一些相关的核心指南
I.3 避免单例
R.5 and R.6 避免非常量全局变量
R.11 避免显式调用 new 和 delete
R.20 and R.21 优先选择 unique_ptr 而不是 shared_ptr 除非你需要分享所有权
理想情况下,您应该有一个工厂模式来创建删除 frame
对象。 frame
的构造函数不得管理地图。这就像公民在管理他们的护照(理想情况下,政府 为 公民这样做)。让经理 class 在地图 (std::map<int,frame>
) 中保留 frame
。管理器 class 提供的方法将创建对象:
class FrameManager
{
std::map<int,frame> Frames;
public:
frame CreateFrame(int id)
{
frame new_frame(id);
Frames[id] = new_frame;
}
};
移除情况如何?那么,根据您的设计和要求,您可以拥有 either/both 个:
- 通过框架的析构函数删除框架。它可能会调用
FrameManager::Remove(id);
- 仅允许 FrameManager 删除 (
FrameManager::Remove(id)
)。我会选择这个(详见下文)。
现在,请注意,使用这种方法,将创建许多对象 frame
(本地、分配给地图、return 等)。您可以使用 shared_ptr<frame>
作为 return 类型
CreateFrame
,并保持 shared_ptr
作为地图类型 map<int, shared_ptr<frame>>
。
使用 shared/unique_ptr
可能看起来很复杂,但它们非常有用。您无需管理生命周期。您可以将相同的 shared_ptr<frame>
传递给多个函数,而无需多次创建 frame
对象。
修改版本:
class FrameManager
{
std::map<int, shared_ptr<frame>> Frames;
public:
shared_ptr<frame> CreateFrame(int id)
{
if(Frames.count(id)>0) return nullptr; // Caller can check shared_ptr state
shared_ptr<frame> new_frame = make_shared<frame>(id);
Frames[id] = new_frame;
return new_frame;
}
};
使用框架管理器将允许完全控制框架创建、更好的错误处理、safe/secure 代码、安全的多线程代码。
我想根据您发布的代码提出几点意见。
- 有些人如何知道
frame_globals::global_frameMap
以及它在不查看源代码的情况下的作用。
- 如果它是一个全局对象那么
frame
class逻辑可以被绕过并且可以从外部修改全局对象。
- 如果有人想以不同的方式管理
frame
对象,或者如果将来某些要求发生变化,例如如果你想允许重复的帧,那么你必须更改 frame
对象。
- 框架class不仅有
frame
相关的数据成员和成员函数,还有管理逻辑。好像没关注single responsibility principle
.
-
copy/move
构造函数呢? std::map/std::set
将影响 frame
class. 的构造函数
我认为 frame
和如何管理之间需要逻辑分离 frames.I 有以下解决方案(我不知道所有用例,我的解决方案基于发布的代码)
frame
class
frame
class 不应该有任何与框架无关的逻辑。
- 如果
frame
对象需要了解其他 frame
对象,则应提供 ContainerWrapper
的引用(不是直接 std::map
或 std::set
,这样更改就不会影响 frame
)
ContainerWrapper
class
ContainerWrapper
应该管理所有 frame
个对象。
- 它可以根据需要有接口,例如
try_emplace
插入元素的returnsiterator
(类似于std::map::try_emplace
),而不是先创建frame
对象,然后尝试插入 std::map
,它可以检查 id
(key) 是否存在,如果 std::map
不存在,则只创建 frame
对象'没有指定 id
. 的对象
关于 std::map/std::set
,
std::set
如果框架对象一旦创建就不需要修改,因为 std::set
不会让你修改框架而不把它取出并重新插入它,或者你必须使用智能指针。
通过这种方式,容器对象可以在多个 frame
对象之间共享,而不必是全局对象。
我使用全球地图来注册一个或多个相同类型的 object。为此,我开始使用全局命名空间。
以此为例(代码未经测试,仅作为示例):
//frame.h
class frame
{
public:
frame(int id);
~frame();
};
namespace frame_globals
{
extern std::map<int, frame *> global_frameMap;
};
//frame.cpp
#include "frame.h"
namespace frame_globals
{
std::map<int, frame *> global_frameMap;
}
frame::frame(int id)
{
//[...]
//assuming this class is exclusively used with "new"!
frame_globals::global_frameMap[id] = this;
}
frame::~frame()
{
frame_globals::global_frameMap.erase(id);
}
这是我使用的一个相当快速的解决方案,但现在我再次偶然发现了用例,我需要注册我的 objects,我问自己是否没有比使用全局变量(我想摆脱它)。
[编辑:似乎不正确]
静态成员变量(据我所知)没有选择,因为静态在每个 object 中都是内联的,我在所有 object 中都需要它。
最好的方法是什么?想到使用常见的 parent,但我想轻松访问我的框架 class。你会怎么解决?
或者您是否知道注册 object 的更好方法,以便我可以从其他地方获得指向它的指针?所以我可能不会完全需要 "new" 并保存 "this"?
我会考虑逆向设计。例如。使用语言内置功能有什么问题?
std::map<int, frame> frames_;
frames_.emplace(id);//adding, ie. creating frames
frames_.erase(id)//removing, ie. deleting frames
现在 creating/deleting 和以前一样简单。巧合的是,如果有人出于不同目的需要框架,这里没有问题。如果 frame 应该是多态的,你可以存储 std::unique_ptr
而不是。
另外,我会考虑让 id
成为 frame 的成员,然后存储在 std::set
而不是 std::map。
class frame
{
public:
int id;
frame(int id);
friend bool operator <(const frame& lhs, const frame& rhs) {return lhs.id < rhs.id;}
};
std::set<frame> frames_;
删除只是:
frames_.erase(frame);
然而,为了根据 id 查找现在有点复杂。幸运的是手边有一个解决方案。这需要实现一个透明的比较,其中包括定义 is_transparent
.
总体而言,您需要考虑所有权。问问自己:在哪里或谁会 own/store map/frame pointers/etc。把它存放在那里。在共享 所有权 的情况下,使用 shared_ptr
(但仅在实际需要时使用此模式 - 这比人们使用它的情况更罕见)。
一些相关的核心指南
I.3 避免单例
R.5 and R.6 避免非常量全局变量
R.11 避免显式调用 new 和 delete
R.20 and R.21 优先选择 unique_ptr 而不是 shared_ptr 除非你需要分享所有权
理想情况下,您应该有一个工厂模式来创建删除 frame
对象。 frame
的构造函数不得管理地图。这就像公民在管理他们的护照(理想情况下,政府 为 公民这样做)。让经理 class 在地图 (std::map<int,frame>
) 中保留 frame
。管理器 class 提供的方法将创建对象:
class FrameManager
{
std::map<int,frame> Frames;
public:
frame CreateFrame(int id)
{
frame new_frame(id);
Frames[id] = new_frame;
}
};
移除情况如何?那么,根据您的设计和要求,您可以拥有 either/both 个:
- 通过框架的析构函数删除框架。它可能会调用
FrameManager::Remove(id);
- 仅允许 FrameManager 删除 (
FrameManager::Remove(id)
)。我会选择这个(详见下文)。
现在,请注意,使用这种方法,将创建许多对象 frame
(本地、分配给地图、return 等)。您可以使用 shared_ptr<frame>
作为 return 类型
CreateFrame
,并保持 shared_ptr
作为地图类型 map<int, shared_ptr<frame>>
。
使用 shared/unique_ptr
可能看起来很复杂,但它们非常有用。您无需管理生命周期。您可以将相同的 shared_ptr<frame>
传递给多个函数,而无需多次创建 frame
对象。
修改版本:
class FrameManager
{
std::map<int, shared_ptr<frame>> Frames;
public:
shared_ptr<frame> CreateFrame(int id)
{
if(Frames.count(id)>0) return nullptr; // Caller can check shared_ptr state
shared_ptr<frame> new_frame = make_shared<frame>(id);
Frames[id] = new_frame;
return new_frame;
}
};
使用框架管理器将允许完全控制框架创建、更好的错误处理、safe/secure 代码、安全的多线程代码。
我想根据您发布的代码提出几点意见。
- 有些人如何知道
frame_globals::global_frameMap
以及它在不查看源代码的情况下的作用。 - 如果它是一个全局对象那么
frame
class逻辑可以被绕过并且可以从外部修改全局对象。 - 如果有人想以不同的方式管理
frame
对象,或者如果将来某些要求发生变化,例如如果你想允许重复的帧,那么你必须更改frame
对象。 - 框架class不仅有
frame
相关的数据成员和成员函数,还有管理逻辑。好像没关注single responsibility principle
. -
copy/move
构造函数呢?std::map/std::set
将影响frame
class. 的构造函数
我认为 frame
和如何管理之间需要逻辑分离 frames.I 有以下解决方案(我不知道所有用例,我的解决方案基于发布的代码)
frame
class
frame
class 不应该有任何与框架无关的逻辑。- 如果
frame
对象需要了解其他frame
对象,则应提供ContainerWrapper
的引用(不是直接std::map
或std::set
,这样更改就不会影响frame
)
ContainerWrapper
class
ContainerWrapper
应该管理所有frame
个对象。- 它可以根据需要有接口,例如
try_emplace
插入元素的returnsiterator
(类似于std::map::try_emplace
),而不是先创建frame
对象,然后尝试插入std::map
,它可以检查id
(key) 是否存在,如果std::map
不存在,则只创建frame
对象'没有指定id
. 的对象
关于 std::map/std::set
,
std::set
如果框架对象一旦创建就不需要修改,因为 std::set
不会让你修改框架而不把它取出并重新插入它,或者你必须使用智能指针。
通过这种方式,容器对象可以在多个 frame
对象之间共享,而不必是全局对象。