I have a class, that has very heavy payload, so that it is very expensive to create/copy/move an instance of this class.
Since they will not change after the app has finished initialization, there is no need to create temporary objects of this class anywere. I only need to cache objects in a container(std::map
), and offer a "const reference" when needed.
The thing that must be emphasized is that I'm seeking a solution which can avoid double-create or unnecessary copy a object before add it into the container(I don't think a solution likes one @getsoubl proposed can resolve the problem, because it does not eliminate doulbe-creating or unnecessary copying).
So I want to arrange the constructor method into "private/protected" section of the class body, so as to forbid any of creating/copying/moving outside of the "Factory-Method". Following is my original solution:
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;
};
But I'm put into a contradiction that the "std::try-emplace" can NOT also call the constructor of MyClass. The compiler reports: "error: ‘MyClass::MyClass(int)’ is private within this context".
So I tried the solution 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;
};
This time I got a error: "use of deleted function ‘MyClass::MyClass()’". I guess that is resulted by "[]" operator of std::map, because it tries to call the default constructor of MyClass.
How can I get it done?
If you want to lock creation down, simply pass a key to everyone allowed in!
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;
};