Как запретить создание объекта на куче?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Запрет создания объектов на куче
В C++ есть несколько способов запретить динамическое выделение памяти (создание объектов на куче с помощью new).
Способ 1: Удалить operator new
Самый простой и надежный способ — явно удалить operator new:
class StackOnly {
public:
StackOnly() = default;
~StackOnly() = default;
// Удаляем operator new
void* operator new(size_t) = delete;
void* operator new[](size_t) = delete;
void operator delete(void*) = delete;
void operator delete[](void*) = delete;
};
int main() {
StackOnly obj1; // OK
StackOnly* obj2 = new StackOnly(); // Ошибка компиляции!
StackOnly arr[10]; // OK
std::vector<StackOnly> v; // OK
}
Это работает потому что компилятор ищет глобальный operator new, но находит удаленную версию.
Способ 2: Приватный operator new (старый стиль)
Вариант для компиляторов без поддержки = delete:
class StackOnly {
pubate:
// Приватные operator new
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void*);
void operator delete[](void*);
public:
StackOnly() = default;
~StackOnly() = default;
};
int main() {
StackOnly obj; // OK
StackOnly* ptr = new StackOnly(); // Ошибка компиляции!
}
Способ 3: Контроль с placement new
Если нужна более гибкая логика:
class StackOnly {
public:
StackOnly() = default;
~StackOnly() = default;
// Удаляем обычные operator new
void* operator new(size_t) = delete;
void* operator new[](size_t) = delete;
// Разрешаем placement new (для специальных случаев)
void* operator new(size_t, void* ptr) = default;
};
int main() {
StackOnly obj; // OK
// new StackOnly(); // Ошибка!
// Но placement new всё равно работает:
char buffer[sizeof(StackOnly)];
StackOnly* ptr = new (buffer) StackOnly(); // OK
}
Способ 4: CRTP (Curiously Recurring Template Pattern)
Для более универсального решения:
template<typename Derived>
class NonHeapAllocatable {
public:
void* operator new(size_t) = delete;
void* operator new[](size_t) = delete;
void operator delete(void*) = delete;
void operator delete[](void*) = delete;
private:
~NonHeapAllocatable() {} // Protected для безопасности
friend Derived;
};
class MyClass : public NonHeapAllocatable<MyClass> {
public:
MyClass() = default;
~MyClass() = default;
};
int main() {
MyClass obj; // OK
MyClass* ptr = new MyClass(); // Ошибка компиляции!
}
Практический пример: RAII с гарантией stack allocation
class DatabaseConnection : public NonHeapAllocatable<DatabaseConnection> {
private:
std::string host;
int port;
std::unique_ptr<Connection> conn;
public:
DatabaseConnection(const std::string& h, int p)
: host(h), port(p) {
conn = std::make_unique<Connection>(host, port);
}
~DatabaseConnection() {
if (conn) {
conn->close();
}
}
void execute(const std::string& query) {
if (!conn) throw std::runtime_error("Not connected");
conn->execute(query);
}
};
int main() {
// Правильное использование (на стеке)
DatabaseConnection db("localhost", 5432);
db.execute("SELECT * FROM users");
// Автоматическая очистка при выходе из области видимости
// Это НЕ скомпилируется:
// DatabaseConnection* badPtr = new DatabaseConnection("localhost", 5432);
}
Почему это полезно
1. RAII гарантия
Объекты на стеке гарантированно вызовут деструктор при выходе из области видимости:
void process() {
MyResource res; // Выделение
// ... использование res ...
// Автоматический вызов деструктора
}
2. Exception safety
Исключения не приведут к утечкам памяти:
void riskyOperation() {
MyResource res; // На стеке
try {
throwException(); // Может выбросить
} catch (...) {
// Деструктор res вызовется автоматически!
throw;
}
}
3. Производительность
Стек обычно быстрее кучи (heap allocation):
// Куча: требует глобальной синхронизации allocator'а
for (int i = 0; i < 1000000; i++) {
auto ptr = std::make_unique<MyClass>(); // Медленно
}
// Стек: просто инкремент указателя стека
for (int i = 0; i < 1000000; i++) {
MyClass obj; // Быстро
}
Проверка на компиляторе
Этот код вызовет ошибку компиляции:
class StackOnly {
public:
void* operator new(size_t) = delete;
};
int main() {
auto ptr = new StackOnly(); // error: use of deleted function
}
Ошибка будет похожа на:
error: use of deleted function 'void* StackOnly::operator new(size_t)'
Вывод
Запрет создания на куче — это важный инструмент для:
- Обеспечения RAII гарантии
- Предотвращения утечек памяти
- Гарантии exception safety
- Улучшения производительности
Используй = delete как стандартный способ в современном C++ (C++11 и выше).