如何在工厂方法之外禁用 creating/copying obj?

How to disable creating/copying obj outside a Factory-Method?

我有一个 class,它的负载非常重,因此 create/copy/move 这个 class 的一个实例非常昂贵。 由于它们在应用程序完成初始化后不会更改,因此无需创建此 class 的临时对象。我只需要在容器中缓存对象(std::map),并在需要时提供"const reference"。

必须强调的是,我正在寻找一种解决方案,可以避免在将对象添加到容器之前进行双重创建或不必要的复制(我不认为@getsoubl 提出的解决方案可以解决问题,因为它不会消除双倍创建或不必要的复制)。

所以我想将构造方法安排到class主体的"private/protected"部分,以禁止"Factory-Method"之外的任何creating/copying/moving。以下是我的原始解决方案:

class MyClass {
public:
   // methods of the class
   static const MyClass & findObject( int iKey ) {
      auto pair = mapObjects.try_emplace( iKey, iKey );
      if ( pair.second )
         cout << "New object has been created" << endl;

      return pair.first->second;
   };

   // deleted
   MyClass() = delete;
   MyClass( MyClass & ) = delete;
   MyClass( MyClass && ) = delete;
   MyClass( const MyClass & ) = delete;
   MyClass( const MyClass && ) = delete;
   MyClass & operator=( MyClass & ) = delete;
   MyClass & operator=( MyClass && ) = delete;
   MyClass & operator=( const MyClass & ) = delete;
   MyClass & operator=( const MyClass && ) = delete;

private:
   // vars of the class
   static map<int, MyClass> mapObjects;

   // vars of instance
   string some_heavy_payload;

   // methods of instance
   MyClass( int iKey ) : 
     some_heavy_payload( std::to_string( iKey ) ) {};
};

map<int, MyClass> MyClass::mapObjects;

int main() {
   const MyClass & obj = MyClass::findObject( 1 );
   return EXIT_SUCCESS;
};

但是我陷入了一个矛盾,即 "std::try-emplace" 不能同时调用 MyClass 的构造函数。 编译器报告:"error: ‘MyClass::MyClass(int)’ is private within this context".

所以我尝试了解决方案2:

class MyClass {
public:
   // methods of the class
   static const MyClass & findObject( int iKey ) {
      if ( mapObjects.find( iKey ) == mapObjects.cend() )
         mapObjects[iKey] = MyClass( iKey );

      return mapObjects[iKey];
   };

   // deleted
   MyClass() = delete;
   MyClass( MyClass & ) = delete;
   MyClass( MyClass && ) = delete;
   MyClass( const MyClass & ) = delete;
   MyClass( const MyClass && ) = delete;
   MyClass & operator=( MyClass & ) = delete;
   MyClass & operator=( const MyClass & ) = delete;
   MyClass & operator=( const MyClass && ) = delete;

private:
   // vars of the class
   static map<int, MyClass> mapObjects;

   // vars of instance
   string some_heavy_payload;

   // methods of instance
   MyClass( int iKey ) {
      some_heavy_payload = std::to_string( iKey );
   };
   MyClass & operator=( MyClass && src ) {
      some_heavy_payload = std::move( src.some_heavy_payload );
      return *this;
   };
};

map<int, MyClass> MyClass::mapObjects;

int main() {
   const MyClass & obj = MyClass::findObject( 1 );

   return EXIT_SUCCESS;
};

这次我得到一个错误:"use of deleted function ‘MyClass::MyClass()’"。 我猜这是由 std::map 的“[]”运算符导致的,因为它试图调用 MyClass.

的默认构造函数

我怎样才能完成它?

您可以通过使用指针映射(复制或移动到映射中的成本很低)而不是对象映射来实现这一点,如果您使用智能指针映射,那么您仍然可以拥有map 管理其中对象的生命周期。

这里有一些 proof-of-concept 代码,可以让您推进这个想法。实际上,createA 和地图将隐藏在一些工厂函数中以填充地图。

请注意 A 的构造函数是私有的,我们不能复制 unique_ptr,只能移动它。此外,make_unique 是 off-limits 因为 A 有一个私有构造函数,但这没什么大不了的,cost-wise。复制指针很便宜。

#include <iostream>
#include <map>
#include <memory>

class A
{
    A () { std::cout << "Create A" << '\n'; }
    A (const A &) = delete;
    A &operator= (const A&) = delete;
    A (A &&) = delete;
    A &operator= (const A&&) = delete;
public:
    ~A () { std::cout << "Destroy A" << '\n'; }
    friend void createA (int key);
};    

static std::map <int, std::unique_ptr <A>> objects;

void createA (int key)
{
    std::unique_ptr <A> a (new A);
    objects.insert (std::pair <int, std::unique_ptr <A>> (key, std::move (a)));
}

int main ()
{
    createA (1);
    createA (2);
}

输出(通过地图显示对象生命周期管理):

Create A
Create A
Destroy A
Destroy A

Live demo


或者,编写一个高效的移动构造函数(这通常并不困难)并将对象移动到地图中而不是复制它们,如下所示:

#include <iostream>
#include <map>
#include <memory>

class A
{
    A () { std::cout << "Create A" << '\n'; }
    A (const A &) = delete;
    A &operator= (const A&) = delete;
    A &operator= (const A&&) = delete;
public:
    A (const A &&) { std::cout << "Move A" << '\n'; }
    ~A () { std::cout << "Destroy A" << '\n'; }
    friend void createA (int key);
};    

static std::map <int, A> objects;

void createA (int key)
{
    A a;
    objects.insert (std::pair <int, A> (key, std::move (a)));
}

int main ()
{
    createA (1);
    createA (2);
}

输出:

Create A
Move A
Move A
Destroy A
Destroy A
Create A
Move A
Move A
Destroy A
Destroy A
Destroy A
Destroy A

Live demo

如果您想锁定创建,只需将密钥传递给允许进入的每个人!

class MyClass {
    class Key {
        Key() = default;
        friend class MyClass;
    };
    MyClass(MyClass const&) = delete;
    MyClass& operator=(MyClass const&) = delete;
    static map<int, MyClass> mapObjects;
public:
    static MyClass const& findObject(int iKey) {
        auto [iter, created] = mapObjects.try_emplace(iKey, Key(), iKey );
        if (created)
            std::cout << "New object has been created\n";
        return iter->second;
    };

    MyClass(Key, int iKey)
    : some_heavy_payload(std::to_string(iKey))
    {}
private:
    string some_heavy_payload;
};