如何为多个堆重载new运算符?

9
在一个具有两个单独的RAM存储区域的嵌入式系统中,我拥有两个不同的堆(一个是FreeRTOS在较低存储区域中的自定义实现,另一个是由GCC在较高存储区域中生成的堆),我希望能够选择new使用哪个堆。
2个回答

13

您可以提供一个重载了operator new的函数,它接受第二个参数告诉它从哪个内存区域分配内存。您可以通过将它们放在新表达式中类型前面的括号中来为operator new提供参数。这通常用于在已经分配的存储空间中new对象(因为这是标准库提供的重载),但任何内容都可以在那里传递,并将传递给operator new

enum MemoryArea {
    LOWER,
    UPPER
};

void* operator new(std::size_t sz, MemoryArea seg) {
    if (seg == LOWER) {
        return allocateMemoryInLowerMemoryArea(sz);
    } else {
        return allocateMemoryInUpperMemoryArea(sz);
    }
}

void operator delete(void* p) {
    if (pointerIsInLowerMemoryArea(p)) {
        freeMemoryFromLowerMemoryArea(p);
    } else {
        freeMemoryFromUpperMemoryArea(p);
    }
}

int main() {
    Foo* p = new (LOWER) Foo;
    Foo* b = new (UPPER) Foo;
    delete p;
    delete b;
}

1
那不是放置 new。放置 new 是在原地实例化对象,不分配内存。 - Captain Obvlious
@CaptainObvlious,这和放置new的语法相同,使用相同的机制,但我想你是对的。我会重新措辞。 - Miles Budnek
2
“它是相同的语法,但不同的语义更为重要。” - Captain Obvlious
2
看起来标准确实将其称为“放置new表达式”,而不管operator new重载实际上如何处理参数。请参见§[expr.new]/13。 - Miles Budnek
Placement new需要size_t和void*,但这个不需要。 - Emilio Garavaglia
2
标准将“放置new表达式”定义为任何提供了“放置new子句”的“new表达式”。 “放置new子句”是指在“new表达式”中类型名称之前的括号表达式列表。虽然这可能仍然不应该以俗语的方式称为放置new,但从技术上讲是正确的。 - Miles Budnek

-4

编辑:请参考已接受的答案,本回答是不正确的 - UseUpperMemoryNew 会影响 MyClass 的分配,而不是 MyClass 函数内部的分配。 保留此内容以供学习/后人/评论。

对于全局命名空间中的较低内存区域

#include <new>
#undef new

void* operator new (std::size_t size) throw (std::bad_alloc) { ... }
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) { ... }
void* operator new[] (std::size_t size) throw (std::bad_alloc) { ... }
void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_constant) throw() { ... }
void operator delete (void* ptr) throw () { ... }
void operator delete (void* ptr, const std::nothrow_t& nothrow_constant) throw() { ... }
void operator delete[] (void* ptr) throw () { ... }
void operator delete[] (void* ptr, const std::nothrow_t& nothrow_constant) throw() { ... }

对于上部内存区域,

void* new2 (std::size_t size) throw (std::bad_alloc) { ... }
void* new2 (std::size_t size, const std::nothrow_t& nothrow_constant) { ... }
void delete2 (void* ptr) throw () { ... }
void delete2 (void* ptr, const std::nothrow_t& nothrow_constant) throw() { ... }

#define UseUpperMemoryNew \
void* operator new (std::size_t size) throw (std::bad_alloc) { return new2(size); }\
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) { return new2(size, nothrow_constant); }\
void* operator new[] (std::size_t size) throw (std::bad_alloc) { return new2(size); }\
void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_constant) throw() { return new2(size, nothrow_constant); }\
void operator delete (void* ptr) throw () { delete2(ptr); }\
void operator delete (void* ptr, const std::nothrow_t& nothrow_constant) throw() { delete2(ptr, nothrow_constant); }\
void operator delete[] (void* ptr) throw () { delete2(ptr); }\
void operator delete[] (void* ptr, const std::nothrow_t& nothrow_constant) throw() { delete2(ptr, nothrow_constant); }

然后,默认情况下使用较低的内存,可以在类级别选择较高的内存:

class MyClass
{
public:
    UseUpperMemoryArea
    void someFunction(); // new operator use in this function uses upper memory area
};

我发现你不能在全局命名空间之外重新定义new - 类级别的重载是唯一的选择。

1
请注意,throw(...)异常说明已弃用 - jaggedSpire
1
这不是类特定的 operator new 重载的工作方式。你的 UseUpperMemoryNew 重载不会在 MyClass 中使用,它们将用于分配新的 MyClass 对象。 - Miles Budnek
2
如果你要发布一个自问自答的帖子,你需要做得比这更好一些。 - Captain Obvlious
发布一个不正确、未被接受的回答到自己的问题中,揭示了其中的缺陷,改进了我的代码,也教会了我一些新的东西。我认为这做得不错。 - Jacob Jennings
1
也许将此内容移至问题正文的“附录”中会是一个好主意。 - Bathsheba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接