#опытным
В этом посте будут не самые обычные способы запретов, которые вряд ли полезны на практике в таком виде, но полезны их отдельные элементы. Да и просто прикольные по своей идее.
Начнем с конца. Жизни объекта, конечно. Давайте сделаем приватным не конструктор, а деструктор. Тогда внешний код не сможет разместить такой объект на стеке, ведь он не сможет его удалить потом.
Однако удалять объект хочется. Для этого сделаем публичный статический метод destroy и фабричный метод, возвращающий умный указатель с кастомным делитером:
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor\n"; }
private:
~MyClass() { std::cout << "MyClass destructor\n"; }
public:
static void destroy(MyClass* ptr) {
delete ptr;
}
static std::unique_ptr<MyClass, decltype(&destroy)> create() {
return std::unique_ptr<MyClass, decltype(&destroy)>(new MyClass(), destroy);
}
};При выходе из скоупа юник разрушится и его деструктор дернет destroy, который сам дернет delete.
Публичный конструктор здесь не имеет большого смысла, но и так и так работает.
Но вообще говоря, зачем все эти запреты? Можно ли как-то добиться запрета при публичном конструкторе и деструкторе?
Можно(почти).
Давайте сделаем публичный деструктор и конструктор с параметром, который не может создать внешний код:
class Key {
private:
Key() = default;
friend class Factory;
};
class Object {
public:
explicit Object(const Key&) {} // требует ключ
};
class Factory {
public:
static std::unique_ptr<Object> create() {
return std::make_unique<Object>(Key());
}
};Технически, все методы класса Object публичные. Но инстанс Key может создать только фабрика. Поэтому и создание инстанса Object возможно только через нее.
Есть даже идиома, называется passkey, которая приписывает примерно так и создавать объекты.
Класс ключа может быть определен и внутри фабрики, как приватный класс. Фабрики может и не быть в принципе, Key может находиться внутри Object. Но суть одна.
И напоследок совсем гадкий утенок. Делаем в заголовке forward declaration типа и объявляем фабричную функцию. В cpp определяем класс и функцию.
// object.hpp
class Object;
std::unique_ptr<Object> createObject();
// object.cpp
#include "object.h"
class Object {
public:
Object() = default;
~Object() = default;
};
std::unique_ptr<Object> createObject() {
return std::make_unique<Object>();
}
В итоге да, мы запретили объекту создаваться, где угодно, кроме как через createObject. Но при этом пользоваться объектом мы никак не сможем. Только создать и удалить.
Explore exotic things. Stay cool.
#design #cppcore