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

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

本文关键字:创建 复制 obj 何禁用 工厂 方法      更新时间:2023-10-16

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

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

所以我想将构造函数方法安排到类体的"私有/受保护"部分,以禁止任何创建/复制/移动到"工厂方法"之外的行为。以下是我最初的解决方案:

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 的默认构造函数。

我怎样才能完成它?

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

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 createdn";
return iter->second;
};
MyClass(Key, int iKey)
: some_heavy_payload(std::to_string(iKey))
{}
private:
string some_heavy_payload;
};

您可以通过使用指针映射(复制或移动到映射中很便宜)而不是对象映射来实现此目的,如果您使用智能指针映射,那么您仍然可以让映射管理其中对象的生存期。

这里有一些概念验证代码,应该让你把这个想法推进。 在实践中,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

现场演示