这是私有构造函数的一个很好的用途吗?
每天尝试学习新东西,我会对以下设计是好是坏感兴趣。
我正在实现一个类 A
,它将自身的对象缓存在静态私有成员变量 std::map<> 中。缓存。
A
的用户应该只能访问指向映射中元素的指针,因为 A
的完整副本非常昂贵且不需要。仅当新的 A
在地图中尚不可用时才会创建,因为 A
的构造需要一些繁重的工作。好的,这是一些代码:
class B;
class A {
public:
static A* get_instance(const B & b, int x) {
int hash = A::hash(b,x);
map<int, A>::iterator found = cache.find(hash);
if(found == cache.end())
found = cache.insert(make_pair(hash, A(b,x))).first;
return &(found->second);
}
static int hash(B & b, int x) {
// unique hash function for combination of b and x
}
// ...
private:
A(B & b, int x) : _b(b), _x(x) {
// do some heavy computation, store plenty of results
// in private members
}
static map<int, A> cache;
B _b;
int _x; // added, so A::hash() makes sense (instead of B::hash())
// ...
};
上面的代码有什么问题吗?有没有什么陷阱, 我是否错过了内存管理问题或其他问题?
感谢您的反馈!
Trying to learn something new every day I'd be interested if the following is good or bad design.
I'm implementing a class A
that caches objects of itself in a static private member variable std::map<> cache
. The user of A
should only have access to pointers to elements in the map, because a full copy of A
is expensive and not needed. A new A
is only created if it is not yet available in the map, as construction of A
needs some heavy lifting. Ok, here's some code:
class B;
class A {
public:
static A* get_instance(const B & b, int x) {
int hash = A::hash(b,x);
map<int, A>::iterator found = cache.find(hash);
if(found == cache.end())
found = cache.insert(make_pair(hash, A(b,x))).first;
return &(found->second);
}
static int hash(B & b, int x) {
// unique hash function for combination of b and x
}
// ...
private:
A(B & b, int x) : _b(b), _x(x) {
// do some heavy computation, store plenty of results
// in private members
}
static map<int, A> cache;
B _b;
int _x; // added, so A::hash() makes sense (instead of B::hash())
// ...
};
Is there anything that is wrong with the code above? Are there any pitfalls,
do I miss memory management problems or anything else?
Thank you for your feedback!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
该实现仅允许您通过 get_instance() 创建项目。理想情况下,您应该将复制构造函数和赋值运算符设为私有。
它不是线程安全的。您可以使用以下方法:
将地图更改为
map
有一个互斥锁并这样使用它:
理想情况下,类中只有 get_instance() 是静态的。其他所有内容都是私有实现细节,并进入类的编译单元,包括 AControl。
请注意,您可以通过锁定在地图中查找和创建的整个过程来更简单地完成此操作,但是在进行漫长的构建过程时,您将锁定更长时间。事实上,一旦插入项目,就会实现记录级锁定。稍后的线程可能会发现该项目未初始化,但 boost::once 逻辑将确保它只创建一次。
The implementation is intended to only allow you to create items via get_instance(). You should ideally make your copy-constructor and assignment operator private.
It would not be thread-safe. You can use the following instead:
Change the map to
map
Have a mutex and use it thus:
Ideally only get_instance() will be static in your class. Everything else is private implementation detail and goes into the compilation unit of your class, including AControl.
Note that you could do this a lot simpler by just locking through the entire process of looking up in the map and creating but then you are locking for longer whilst you do the long construction process. As it is this implements record-level locking once you have inserted the item. A later thread may find the item uninitialised but the
boost::once
logic will ensure it is created exactly once.每当您使用全局变量(在本例中为静态映射)时,如果跨多个线程使用全局变量,您都必须担心并发问题。例如,如果两个线程尝试同时获取特定实例,它们都可能创建一个对象,从而导致重复。更糟糕的是,如果他们都尝试同时更新地图,地图可能会被损坏。您必须使用互斥体来控制对容器的访问。
如果它只是单线程,那么在有人决定将来需要将其设为多线程之前,不会有任何问题。
另外作为风格说明,虽然以下划线+小写字母开头的名称在技术上是合法的,但避免任何以下划线开头的符号将避免可能意外违反规则并出现奇怪的行为。
Any time you use globals (in this case the static map) you have to worry about concurrency issues if this is used across multiple threads. For example, if two threads were trying to get a particular instance at once, they could both create an object resulting in duplicates. Even worse, if they both tried to update the map at the same time it could get corrupted. You'd have to use mutexes to control access to the container.
If it's single-threaded only then there's no issue until someone decides it needs to be made multi-threaded in the future.
Also as a style note, while names starting with underscore+lower case letter are technically legal, avoid any symbols starting with underscores will avoid possibly accidentally breaking the rules and getting weird behavior.
我认为这是在 A 中混合在一起的 3 个独立的东西:
我认为它们应该在代码中分开,而不是全部放在 A 中。
这意味着:
编写类 A 时不考虑它应该如何分配。
编写一个通用模块来执行对象的池缓存,大致如下:
*
*
I think these are 3 separate things that you mix together inside A:
I think they should be separate in the code, not all together inside A.
That means:
write your class A without any consideration of how it should be allocated.
write a generic module to perform pool cache of objects, along the lines of:
*
*