Как запретить создание объекта на куче (heap) в C++

Ответ

Чтобы запретить создание объекта в динамической памяти (куче) с помощью оператора new, нужно удалить (или сделать приватными) соответствующие операторы new.

Современный способ (C++11 и новее): = delete

class StackOnly {
public:
    StackOnly() = default;
    // Удаляем операторы new
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete;
    // Также можно удалить placement new для полноты
    void* operator new(std::size_t, void*) = delete;
};

int main() {
    StackOnly obj;          // OK: объект на стеке
    // StackOnly* p = new StackOnly();   // Ошибка: 'operator new' is deleted
    // StackOnly* arr = new StackOnly[5]; // Ошибка: 'operator new[]' is deleted
    return 0;
}

Устаревший способ (до C++11): приватные операторы new

class StackOnlyOld {
private:
    void* operator new(std::size_t);      // Не определяем
    void* operator new[](std::size_t);    // Не определяем
public:
    StackOnlyOld() {}
};

Важные нюансы:

  • Этот метод не препятствует созданию объекта в статической памяти (глобальные/статические переменные) или на стеке.
  • Он не препятствует созданию объекта как члена другого класса, который сам создается в куче.
  • Основная цель — предотвратить явное использование new для данного типа, что полезно для контроля над временем жизни объектов или для классов, управляющих критичными ресурсами.

Ответ 18+ 🔞

А, слушай, вот тебе история про то, как заставить объект сидеть на стеке и не высовываться. Представь себе класс — такой зазнайка, который говорит: «Я, блядь, слишком хорош для этой помойки под названием куча, создавай меня только на стеке, ёпта». И это реально можно устроить, если наглухо забанить операторы new.

Нормальный, современный способ (C++11 и дальше): просто удали их нахуй

class StackOnly {
public:
    StackOnly() = default;
    // Вот тут просто берём и вычёркиваем операторы new из жизни класса
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete;
    // Для полного параноидального счастья можно и placement new прибить
    void* operator new(std::size_t, void*) = delete;
};

int main() {
    StackOnly obj;          // Окей, живёшь на стеке — молодец
    // StackOnly* p = new StackOnly();   // Ошибка компиляции: оператор new удалён, иди нахуй
    // StackOnly* arr = new StackOnly[5]; // Та же песня, нихуя не выйдет
    return 0;
}

Старый дедовский способ (для мазохистов и легаси-кода): спрятать в приват

class StackOnlyOld {
private:
    void* operator new(std::size_t);      // Объявили приватным и даже не определяем — пусть линкер матерится
    void* operator new[](std::size_t);    // Тоже самое
public:
    StackOnlyOld() {}
};

Но есть нюансы, ёб твою мать:

  • Этот фокус не мешает создать объект как глобальную или статическую переменную. То есть он не строго стековый, он просто против кучи.
  • Он не помешает, если этот объект будет полем другого класса, а тот уже создадут через new. Получится, что наш красавец всё равно окажется в куче, но опосредованно — хитрая жопа.
  • Смысл всей этой движухи — жёсткий контроль. Например, для классов, которые работают с какими-нибудь ресурсами, которые нельзя копировать или которые должны умирать ровно тогда, когда вышли из scope. Чтобы какой-нибудь распиздяй не нагородил new и не забыл delete. Будет вам хиросима и нагасаки, а потом удивление — пиздец, утечка.