C++ 这是私有构造函数的良好用途吗?

C++ 这是私有构造函数的良好用途吗?,c++,private-constructor,C++,Private Constructor,尝试每天学习一些新的东西,我会感兴趣的,如果以下是好的或坏的设计 我正在实现一个类a,它将自身的对象缓存在一个静态私有成员变量std::map cache中。A的用户应该只能访问指向映射中元素的指针,因为A的完整副本价格昂贵且不需要。新的A仅在地图中尚不可用时创建,因为A的构建需要一些繁重的工作。好的,这里有一些代码: class B; class A { public: static A* get_instance(const B & b, int x) {

尝试每天学习一些新的东西,我会感兴趣的,如果以下是好的或坏的设计

我正在实现一个类
a
,它将自身的对象缓存在一个静态私有成员变量
std::map cache
中。
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())
        // ...
 };
B类;
甲级{
公众:
静态A*get_实例(常量B&B,int x){
int hash=A::hash(b,x);
map::iterator found=cache.find(散列);
if(found==cache.end())
found=cache.insert(生成_对(散列,A(b,x)))。首先;
返回&(找到->第二个);
}
静态整数散列(B&B,intx){
//b和x组合的唯一哈希函数
}
// ...
私人:
A(B&B,int x):_B(B),_x(x){
//做一些繁重的计算,存储大量的结果
//私下成员
}
静态地图缓存;
B_B;
int x;//添加了,因此A::hash()有意义(而不是B::hash())
// ...
};
上面的代码有什么问题吗?有没有陷阱,, 我是否错过了内存管理问题或其他问题

感谢您的反馈

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

如果它是单线程的,那么就没有问题了,直到有人决定它将来需要多线程


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

该实现仅允许您通过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;
  }
};
理想情况下,类中只有get_instance()是静态的。其他所有内容都是私有实现细节,并进入类的编译单元,包括AControl


请注意,您可以通过在地图中查找和创建的整个过程中锁定来实现这一点,但在执行漫长的构建过程时,锁定的时间更长。事实上,一旦插入了项,就会实现记录级锁定。稍后的线程可能会发现该项未初始化,但
boost::once
逻辑将确保该项只创建一次。

我认为在一个

  • A级本身(它的入口应该做什么)
  • 用于缓存目的的实例池
  • 对于某种类型,有这样一个静态的singlton池
我认为它们在代码中应该是分开的,而不是在A中一起

这意味着:

  • 写你的A班,不考虑如何分配

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

*

模板类PoolHashKey{…};
模板类池缓存
{  
//资料
私有:标准::映射<..>映射;
//方法
public:templatePoolKeyget_实例(B);
public:void release_实例(PoolKey);
//请注意,这些不是静态函数成员
};  
  • 在某处创建PoolCache的单例实例并使用它:
*

PoolCache&myAPool()
{  
静态缓存池;
返回s;
//你应该使用一些安全的单例习惯用法。
}  
int main()
{  
B B;
PoolKey const aKey(myAPool().get_实例(b);
常数A(aKey.get());
//...  
myAPool().release_instance(aKey);//不再使用它
/*或者PoolKey的析构函数可能应该进行一些引用计数,并让池知道不再需要此Instance*/
}  

事实上,这是一个错误,已经修复。为什么要返回一个引用?为了让界面看起来更简单?指针对调用者说它可能是空的,没有考虑多线程。然后,我想,需要一个互斥体并在get_实例()中使用锁方法?您声明复制是昂贵的,但您正在创建一个,然后将其复制到Map@jimmy你是对的,但是对象必须“活”在某个地方,对吗?如果我使用<代码> map < /Cord>谁拥有新的<代码> < < /代码>?对不起,如果有明显的答案……我倾向于考虑一些描述的智能指针。(boost::shared_ptr可以与容器一起使用)。如果您使用的编译器支持右值引用,则提供一个移动构造函数将不会产生任何问题。事实上,在我的实现中,可以将带有互斥体和控件类的映射放入某种模板中,尽管您必须为其提供构造函数。也许这样做并将其提交给boost?
AControl * ctrl;
{
  mutex::scoped_lock lock(mtx);
  ctrl = &cache[hash];
}
return ctrl->get(b,x);
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<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*/
}