我有一个要渲染的抽象对象的容器。我通常会将
std::unique_ptr<T>
或 std::shared_ptr<T>
存储在容器内,但我不想堆分配每个元素,因为这些类 new
是一个对象。
例如,如果所有元素都是非拥有的,则多态性无需堆分配也是可能的:
plf::hive<Base*> objects;
Derived t;
objects.insert(&t);
我希望我的容器可能拥有它的一些对象。我当前的解决方案只是存储
std::variant<T*, std::unique_ptr<T>>
,因此任何拥有的对象都会被正确销毁,并且不管理非拥有对象的生命周期。但我仍然在堆分配需要管理其生命周期的对象。纯粹基于堆栈的多态性可能吗?
编辑:我很乐意为要存储的对象分配堆空间。我只是不想在堆上创建对象本身。
您的问题围绕着在不堆分配对象本身的情况下实现多态性,并且您正在探索允许某些对象由容器拥有而其他对象不属于容器的替代方案。看来您正在寻找一种方法来使对象具有多态行为,这些对象要么位于堆栈上,要么位于预分配的内存池中。
解决此问题的一种方法是使用一种称为“类型擦除”的技术,该技术允许多态行为,而不必为每个对象使用堆分配。然而,这通常仍然涉及类型擦除机制本身的一些堆分配。
另一种方法是使用连续缓冲区(如 std::array 或原始数组)作为内存池,您可以使用placement new 来放置对象。这种方法需要仔细管理对象的生存期和对齐,但可以避免每个对象的堆分配。
这是一个概念性示例 -
#include <iostream>
#include <array>
#include <new> // for placement new
class Base {
public:
virtual void doSomething() = 0;
virtual ~Base() = default;
};
class Derived1 : public Base {
public:
void doSomething() override {
std::cout << "Derived1 doing something\n";
}
};
class Derived2 : public Base {
public:
void doSomething() override {
std::cout << "Derived2 doing something\n";
}
};
template <size_t N>
class ObjectPool {
std::array<std::byte, N> buffer;
size_t currentIndex = 0;
public:
template <typename T, typename... Args>
T* createObject(Args&&... args) {
if (currentIndex + sizeof(T) <= N) {
T* object = new (&buffer[currentIndex]) T(std::forward<Args>(args)...);
currentIndex += sizeof(T);
return object;
} else {
throw std::bad_alloc();
}
}
};
int main() {
ObjectPool<1024> pool;
Base* obj1 = pool.createObject<Derived1>();
Base* obj2 = pool.createObject<Derived2>();
obj1->doSomething();
obj2->doSomething();
// Manually call destructors since we used placement new
obj1->~Base();
obj2->~Base();
return 0;
}