如何实现一个具有放置新和就位功能的简单容器?
我需要实现一个容器来容纳大量元素,并且由于某种原因,它必须在没有任何堆分配的情况下工作.另一个要求是,不应以任何方式复制或移动容器元素.它们必须直接构造到容器分配的内存中.
I need to implement a container to hold an amount of elements and for some reason, it has to work without any heap allocation. Another requirement is, that the container elements should not be copied or moved in any way. They have to constructed directly into the memory allocated by the container.
为此,我决定使用 Placement new 并将内存管理完全委托给容器实现(在 drdobbs).
For that, I decided to use placement new and delegate the memory management completely to the container implementation (found some useful information about placement new at drdobbs).
此处提供了一个运行示例.(请注意,使用 new uint8_t[size]
和 std::queue
只是为了保持示例简单.我的真实代码更复杂,无堆而是实现.)
A running example is found here.
(Please note, that the use of new uint8_t[size]
and std::queue
is just to keep the example simple. My real code has more complex, heap-less implementation instead.)
到目前为止,这非常有效,因为客户端代码必须通过以下调用将元素放入容器中:
This perfectly works so far, as the client code has to put elements into the container with calls like:
executer.push(new (executer) MyRunnable("Hello", 123));
现在我想在此语句中删除重复写入 execute
的需要.我宁愿写一些类似的东西,例如:
Now I want do remove the need of the repeated write executer
in this statement. I would rather like to write something like e.g.:
executer.pushNew(MyRunnable("Hello", 123));
或
executer.pushNew(MyRunnable, "Hello", 123);
也许通过提供一个合适的模板,但我没有写一个(请不要使用预处理器宏).
maybe by providing an appropriate template but I failed to write one (no preprocessor macros, please).
我在 std::allocator 的有用信息/184403759" rel="noreferrer">drdobbs 但不知道如何将其应用于我的问题(此外,这篇文章是 anno 2000 的,所以不要利用可能的 C++11 优势).
I'd found some useful information about std::allocator
here at drdobbs but don't know how to apply it to my problem (further, the article is of anno 2000 and so don't take use of possible C++11 advantages).
有人能帮我找到一种不再需要给 execute
两次的方法吗?
Could one help me to find a way to not longer need to give the executer
twice?
在成功批准 Jarod42 的回答后,我更新了我的跑步示例代码这里.
After successful approving Jarod42's answer, I'd updated my running example code here.
对于历史,这里是我最初问题的原始示例代码:
And for the history, here the original example code of my initial question:
#include <iostream>
#include <queue>
class Runnable {
// Runnable should be uncopyable and also unmovable
Runnable(const Runnable&) = delete;
Runnable& operator = (const Runnable&) = delete;
Runnable(const Runnable&&) = delete;
Runnable& operator = (const Runnable&&) = delete;
public:
explicit Runnable() {}
virtual ~Runnable() {}
virtual void run() = 0;
};
class MyRunnable: public Runnable {
public:
explicit MyRunnable(const char* name, int num): name(name), num(num) {}
virtual void run() override {
std::cout << name << " " << num << std::endl;
}
private:
const char* name;
int num;
};
class Executer {
// Executer should be uncopyable and also unmovable
Executer(const Executer&) = delete;
Executer& operator = (const Executer&) = delete;
Executer(const Executer&&) = delete;
Executer& operator = (const Executer&&) = delete;
public:
explicit Executer() {
}
void* allocateEntry(size_t size) {
// this heap allocation is just to keep this example simple
// my real implementation uses it's own memory management instead (blockpool)
return new uint8_t[size];
}
void push(Runnable* entry) {
queue.push(entry);
}
template <typename R> // this don't works
void pushNew(R) {
push(new (*this) R);
}
inline friend void* operator new(size_t n, Executer& executer) {
return executer.allocateEntry(n);
}
void execute() {
while (queue.size() > 0) {
Runnable* entry = queue.front();
queue.pop();
entry->run();
// Now doing "placement delete"
entry->~Runnable();
uint8_t* p = reinterpret_cast<uint8_t*>(entry);
delete[] p;
}
}
private:
// this use of std::queue is just to keep this example simple
// my real implementation uses it's own heap-less queue instead
std::queue<Runnable*> queue {};
};
int main() {
Executer executer;
executer.push(new (executer) MyRunnable("First", 1));
executer.push(new (executer) MyRunnable("Second", 2));
executer.push(new (executer) MyRunnable("Third", 3));
// but want to use it more like one this
//executer.pushNew(MyRunnable("Fifth", 5)); // how to implement it?
//executer.pushNew(MyRunnable, "Sixth", 6); // or maybe for this usage?
executer.execute();
}
推荐答案
With:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
push(new (*this) R(std::forward<Ts>(args)...));
}
你可以写:
executor.PushNew<MyRunnable>("Hello", 123);
而不是
executer.push(new (executer) MyRunnable("Hello", 123));
相关文章