TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.36K subscribers
Post #1060 4.36K
Как запретить объекту создаваться на стеке? Экзотика
#опытным

В этом посте будут не самые обычные способы запретов, которые вряд ли полезны на практике в таком виде, но полезны их отдельные элементы. Да и просто прикольные по своей идее.

Начнем с конца. Жизни объекта, конечно. Давайте сделаем приватным не конструктор, а деструктор. Тогда внешний код не сможет разместить такой объект на стеке, ведь он не сможет его удалить потом.

Однако удалять объект хочется. Для этого сделаем публичный статический метод 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
  • 🔥 17
  • ❤ 8
  • 👍 8
  • 🤯 1
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →