题
有谁知道如何在独立于平台的 C++ 代码中防止在堆上创建对象?也就是说,对于“Foo”类,我想阻止用户这样做:
Foo *ptr = new Foo;
并且只允许他们这样做:
Foo myfooObject;
有人有什么想法吗?
干杯,
解决方案
尼克的回答 是一个很好的起点,但不完整,因为您实际上需要重载:
private:
void* operator new(size_t); // standard new
void* operator new(size_t, void*); // placement new
void* operator new[](size_t); // array new
void* operator new[](size_t, void*); // placement array new
(良好的编码实践建议您还应该重载delete和delete[]运算符——我会的,但因为它们不会被调用,所以它不会 真的 必要的。)
保罗杜 这也是正确的,尽管它确实可以从 Foo 继承,但它无法在 Foo 上聚合。您可以执行一些模板元编程魔法来帮助防止这种情况,但它不会免受“邪恶用户”的影响,因此可能不值得复杂化。关于如何使用它的文档,以及确保正确使用它的代码审查,是唯一 100% 的方法。
其他提示
您可以为 Foo 重载 new 并将其设为私有。这意味着编译器会抱怨......除非您从 Foo 内部的堆上创建 Foo 的实例。要捕获这种情况,您可以简单地不编写 Foo 的新方法,然后链接器会抱怨未定义的符号。
class Foo {
private:
void* operator new(size_t size);
};
附言。是的,我知道这可以很容易地规避。我真的不推荐它 - 我认为这是一个坏主意 - 我只是回答问题!;-)
我不知道如何以可靠且便携的方式做到这一点。但..
如果该对象位于堆栈上,那么您可以在构造函数中断言“this”的值始终接近堆栈指针。如果是这种情况,该对象很有可能会在堆栈上。
我相信并非所有平台都以相同的方向实现其堆栈,因此当应用程序开始验证堆栈增长的方式时,您可能需要进行一次性测试。或者做一些软糖:
FooClass::FooClass() {
char dummy;
ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
if (displacement > 10000 || displacement < -10000) {
throw "Not on the stack - maybe..";
}
}
@缺口
这可以通过创建一个派生自 Foo 或聚合 Foo 的类来规避。我认为我的建议(虽然不稳健)仍然适用于派生类和聚合类。
例如:
struct MyStruct {
Foo m_foo;
};
MyStruct* p = new MyStruct();
在这里,我在堆上创建了一个“Foo”实例,绕过了 Foo 的隐藏 new 运算符。
因为调试标头可以覆盖运算符新签名,所以最好使用...签名作为完整的补救措施:
private:
void* operator new(size_t, ...) = delete;
void* operator new[](size_t, ...) = delete;
您可以在 Foo 类中声明一个名为“operator new”的函数,该函数将阻止对 new 的正常形式的访问。
这是您想要的行为吗?
您可以将其声明为接口,并从您自己的代码中更直接地控制实现类。
可以通过将构造函数设置为私有并提供静态成员来在堆栈中创建对象来防止这种情况
Class Foo
{
private:
Foo();
Foo(Foo& );
public:
static Foo GenerateInstance() {
Foo a ; return a;
}
}
这将使对象的创建始终在堆栈中。
不确定这是否提供了任何编译时机会,但是您是否考虑过为您的类重载“new”运算符?