如何禁用在工厂方法外创建/复制obj?

时间:2019-05-29 09:08:00

标签: c++ factory factory-pattern move-constructor emplace

我有一个类,它的负载非常重,因此创建/复制/移动该类的实例非常昂贵。 由于在应用程序完成初始化之后它们将不会更改,因此无需创建此类的临时对象。我只需要将对象缓存在容器(std::map)中,并在需要时提供“常量引用”。

必须强调的是,我正在寻找一种可以避免在将对象添加到容器之前重复创建或不必要复制对象的解决方案(我认为像提出的@getsoubl这样的解决方案无法解决该问题。问题,因为它不能消除创建doulbe或不必要的复制。

因此,我想将构造函数方法安排在类主体的“私有/受保护”部分中,以禁止在“ Factory-Method”之外创建/复制/移动任何方法。以下是我的原始解决方案:

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的构造函数。 编译器报告:“错误:‘MyClass :: MyClass(int)’在此上下文中是私有的。”

所以我尝试了解决方案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;
};

这次,我得到一个错误:“使用已删除的功能'MyClass :: MyClass()'”。 我猜这是由std :: map的“ []”运算符导致的,因为它试图调用MyClass的默认构造函数。

如何完成?

2 个答案:

答案 0 :(得分:2)

如果您想锁定创建,只需将密钥传递给所有允许的人!

class MyClass {
    struct Key {}; // This is the Key!
    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;
};

答案 1 :(得分:0)

您可以通过使用指针映射(便宜地将其复制或移动到地图中)而不是对象映射来实现此目的,并且,如果您使用智能指针映射,则仍可以使用地图管理其中对象的生命周期。

这里有一些概念验证代码,应该可以使您继续前进。实际上,createA和地图将隐藏在某些工厂函数中以填充地图。

请注意,A的构造函数是私有的,我们不能复制unique_ptr,只能移动它。另外,make_unique是禁止进入的,因为A具有私有构造函数,但这在成本上并不重要。复制指针很便宜。

#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


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

#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