#новичкам
Про стек поговорили, не будем и кучу обижать.
Начать можно со знакомого подхода: приватный конструктор и фабрика, возвращающая объект по значению:
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