这是私有构造函数的一个很好的用途吗?

发布于 2024-10-19 12:22:29 字数 1157 浏览 7 评论 0原文

每天尝试学习新东西,我会对以下设计是好是坏感兴趣。

我正在实现一个类 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜`诱少女 2024-10-26 12:22:29

该实现仅允许您通过 get_instance() 创建项目。理想情况下,您应该将复制构造函数和赋值运算符设为私有。

它不是线程安全的。您可以使用以下方法:

const boost::once_flag BOOST_ONCE_INIT_CONST = BOOST_ONCE_INIT;

struct AControl
{
  boost::once_flag onceFlag;
  shared_ptr<A> aInst;

  void create( const B&b, int x )
  {
      aInst.reset( new A(b, x) );
  }

  AControl() : onceFlag( BOOST_ONCE_INIT_CONST )
  {
  }

  A& get( const B&b, int x )
  {
     boost::call_once( onceFlag, bind( &AOnceControl::create, this, b, x ) );
     return *aInst;
  }
};

将地图更改为
map

有一个互斥锁并这样使用它:

AControl * ctrl;
{
  mutex::scoped_lock lock(mtx);
  ctrl = &cache[hash];
}
return ctrl->get(b,x);

理想情况下,类中只有 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:

const boost::once_flag BOOST_ONCE_INIT_CONST = BOOST_ONCE_INIT;

struct AControl
{
  boost::once_flag onceFlag;
  shared_ptr<A> aInst;

  void create( const B&b, int x )
  {
      aInst.reset( new A(b, x) );
  }

  AControl() : onceFlag( BOOST_ONCE_INIT_CONST )
  {
  }

  A& get( const B&b, int x )
  {
     boost::call_once( onceFlag, bind( &AOnceControl::create, this, b, x ) );
     return *aInst;
  }
};

Change the map to
map

Have a mutex and use it thus:

AControl * ctrl;
{
  mutex::scoped_lock lock(mtx);
  ctrl = &cache[hash];
}
return ctrl->get(b,x);

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.

围归者 2024-10-26 12:22:29

每当您使用全局变量(在本例中为静态映射)时,如果跨多个线程使用全局变量,您都必须担心并发问题。例如,如果两个线程尝试同时获取特定实例,它们都可能创建一个对象,从而导致重复。更糟糕的是,如果他们都尝试同时更新地图,地图可能会被损坏。您必须使用互斥体来控制对容器的访问。

如果它只是单线程,那么在有人决定将来需要将其设为多线程之前,不会有任何问题。

另外作为风格说明,虽然以下划线+小写字母开头的名称在技术上是合法的,但避免任何以下划线开头的符号将避免可能意外违反规则并出现奇怪的行为。

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.

绝不服输 2024-10-26 12:22:29

我认为这是在 A 中混合在一起的 3 个独立的东西:

  • A 类本身(它的实例应该做什么)。
  • 出于缓存目的而对实例进行池化,
  • 对于某种类型具有这样的静态单例池,

我认为它们应该在代码中分开,而不是全部放在 A 中。

这意味着:

  • 编写类 A 时不考虑它应该如何分配。

  • 编写一个通用模块来执行对象的池缓存,大致如下:

*

template< typename T > class PoolHashKey { ... };

template< typename T > class PoolCache  
{  
//data  
  private: std::map< .... > map_;  

//methods  
    public: template< typename B > PoolKey< T > get_instance( B b );  
    public: void release_instance( PoolKey< T > );  
    // notice that these aren't static function members  
};  
  • 在某处创建一个 PoolCache 的单例实例并使用它:

*

PoolCache<A>& myAPool()  
{  
    static PoolCache<A> s;  
    return s;  
    //you should use some safe singleton idiom.  
}  

int main()  
{  
  B b;  
  PoolKey<A> const aKey( myAPool().get_instance( b );  
  A* const a( aKey.get() );  
  //...  
  myAPool().release_instance( aKey ); //not using it anymore
  /*or else the destructor of PoolKey<A> should probably do some reference count and let the pool know this instace isn't needed anymore*/
}  

I think these are 3 separate things that you mix together inside A:

  • the class A itself (what its intances are supposed to do).
  • poolling of instances for cache purposes
  • having such a static singlton pool for a certain type

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:

*

template< typename T > class PoolHashKey { ... };

template< typename T > class PoolCache  
{  
//data  
  private: std::map< .... > map_;  

//methods  
    public: template< typename B > PoolKey< T > get_instance( B b );  
    public: void release_instance( PoolKey< T > );  
    // notice that these aren't static function members  
};  
  • create a singleton instance of PoolCache somewhere and use it:

*

PoolCache<A>& myAPool()  
{  
    static PoolCache<A> s;  
    return s;  
    //you should use some safe singleton idiom.  
}  

int main()  
{  
  B b;  
  PoolKey<A> const aKey( myAPool().get_instance( b );  
  A* const a( aKey.get() );  
  //...  
  myAPool().release_instance( aKey ); //not using it anymore
  /*or else the destructor of PoolKey<A> should probably do some reference count and let the pool know this instace isn't needed anymore*/
}  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文