Как отключить создание / копирование объекта за пределами Factory-Method? - PullRequest
1 голос
/ 29 мая 2019

У меня есть класс, который имеет очень большую полезную нагрузку, поэтому создание / копирование / перемещение экземпляра этого класса обходится очень дорого.Поскольку они не изменятся после завершения инициализации приложения, нет необходимости создавать временные объекты этого класса.Мне нужно только кэшировать объекты в контейнере (std::map) и предлагать «константную ссылку», когда это необходимо.

Следует подчеркнуть, что я ищу решение, которое может избежать двойногосоздать или сделать ненужное копирование объекта перед тем, как добавить его в контейнер (я не думаю, что решение, которое нравится одному из предложенных @getsoubl, может решить проблему, поскольку оно не устраняет создание doulbe или ненужное копирование).

ИтакЯ хочу разместить метод конструктора в разделе «private / protected» тела класса, чтобы запретить любое создание / копирование / перемещение за пределы «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 ]

4 голосов
/ 29 мая 2019

Если вы хотите заблокировать создание, просто передайте ключ всем, кому разрешено войти!

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;
};
0 голосов
/ 29 мая 2019

Вы можете достичь этого, используя карту указателей (которые дешево копировать или перемещать на карту), а не карту объектов, и если вы используете карту умных указателей, вы все равно можете управлять картой. время жизни объектов в нем.

Вот некоторый проверочный код, который должен помочь вам продвинуть идею. На практике 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

Живая демоверсия


В качестве альтернативы , напишите эффективный конструктор перемещения (что обычно не сложно) и переместите ваши объекты на карту, а не копируйте их, например:

#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 демо

...