TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #1061 4.28K
Как запретить объекту создаваться на куче?
#новичкам

Про стек поговорили, не будем и кучу обижать.

Начать можно со знакомого подхода: приватный конструктор и фабрика, возвращающая объект по значению:

class OnlyStack {
OnlyStack() = default;
public:
static OnlyStack Create() { return {}; }
};


Если объект может быть создан некорректно, то вернуть можно std::optional, с помощью которого ошибку можно будет отловить:

class OnlyStack {
OnlyStack() {
if (rand() % 2) {
std::cout << "ERROR" << std::endl;
}
}
public:
static std::optional<OnlyStack> Create() { return {}; }
};


"На приватных конструкторах мы уже собаку съели. Даешь способ по-интереснее!"

Хорошо. Давайте будем радикальными. Просто удалим перегрузку оператора new для этого класса. Тогда вообще никто не будет в состоянии объект на куче создать:

class OnlyStack {
public:
OnlyStack() = default;
~OnlyStack() = default;

static void* operator new(std::size_t) = delete;
static void* operator new = delete;
};

OnlyStack obj; // OK
OnlyStack* p = new OnlyStack(); // ERROR


Вот так просто и без дополнительных приседаний.

Но можно играть не радикально, а хитро. Не удалим, а переопределим operator new так, чтобы он размещал объекты на готовом существующем статическом буфере. Реально рабочий примерчик довольно большой будет, плюс чтобы не углубляться в пучины кастомных аллокаторов, покажем только идею:

class PooledObject {
static char pool[1024];
static size_t offset;
public:
void* operator new(size_t s) {
if (offset + s > 1024) throw std::bad_alloc();
void* ptr = pool + offset;
offset += s;
return ptr;
}
void operator delete(void*) noexcept {
// Complicated logic
// or just ignore freeing memory
}
};
PooledObject* obj = new PooledObject();


Типа линейный аллокатор, при запросе нового объекта просто сдвигаем offset буфера.

Это конечно все интересно. Но вспомните пост, где мы разбирали вопрос "Где аллоцируются элементы std::array?" и задумайтесь. А что если целевой объект будет полем другого класса, который мы создаем на куче?

Тогда его расположение будет определяться тем, где находится объемлющий объект. То есть, если мы создаём объект Container на куче, то и все его поля, включая OnlyStack, окажутся на куче. Получается, что наш запрет на new OnlyStack не спасает от ситуации, когда OnlyStack становится членом другого класса, который кто-то создаёт через new.

class OnlyStack {
OnlyStack() = default;
public:
static OnlyStack Create() { return {}; }
};

struct Container {
Container() : obj{OnlyStack::Create()} {}
OnlyStack obj;
};

Container* p = new Container(); // OK


Пишите в комментах, если знаете, как обойти эту проблему)

Don't be so radical. Stay cool.

#cppcore #memory #design
  • 👍 19
  • ❤ 10
  • 🔥 9
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 →