30

有谁知道我可以如何在独立于平台的 C++ 代码中阻止在堆上创建对象?也就是说,对于“Foo”类,我想阻止用户这样做:

Foo *ptr = new Foo;

并且只允许他们这样做:

Foo myfooObject;

有没有人有任何想法?

干杯,

4

9 回答 9

28

尼克的回答是一个很好的起点,但不完整,因为您实际上需要重载:

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[] 运算符——我会的,但因为它们不会被调用,所以实际上没有必要。)

Pauldoo也是正确的,它不能在 Foo 上进行聚合,尽管它确实可以从 Foo 继承。你可以做一些模板元编程魔法来帮助防止这种情况,但它不会对“邪恶的用户”免疫,因此可能不值得复杂化。应该如何使用它的文档,以及确保正确使用它的代码审查,是唯一约 100% 的方法。

于 2008-08-14T16:36:41.233 回答
10

您可以为 Foo 重载 new 并将其设为私有。这意味着编译器会呻吟……除非您从 Foo 中在堆上创建 Foo 的实例。要抓住这种情况,您可以简单地不编写 Foo 的新方法,然后链接器就会抱怨未定义的符号。

class Foo {
private:
  void* operator new(size_t size);
};

PS。是的,我知道这很容易绕过。我真的不推荐它——我认为这是个坏主意——我只是在回答这个问题!;-)

于 2008-08-14T13:31:05.587 回答
7

我不知道如何可靠和便携地做到这一点..但是..

如果对象在堆栈上,那么您可以在构造函数中断言“this”的值总是接近堆栈指针。如果是这种情况,对象很有可能会在堆栈上。

我相信并非所有平台都以相同的方向实现其堆栈,因此您可能希望在应用程序开始验证堆栈增长方式时进行一次性测试。或者做一些软糖:

FooClass::FooClass() {
    char dummy;
    ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
    if (displacement > 10000 || displacement < -10000) {
        throw "Not on the stack - maybe..";
    }
}
于 2008-08-14T13:37:17.680 回答
3

@缺口

这可以通过创建一个派生自或聚合 Foo 的类来规避。我认为我的建议(虽然不健全)仍然适用于派生类和聚合类。

例如:

struct MyStruct {
    Foo m_foo;
};

MyStruct* p = new MyStruct();

在这里,我在堆上创建了一个 'Foo' 的实例,绕过了 Foo 的隐藏 new 运算符。

于 2008-08-14T13:39:54.787 回答
2

因为调试头可以覆盖 operator new 签名,所以最好使用 ... 签名作为一个完整的补救措施:

private:
void* operator new(size_t, ...) = delete;
void* operator new[](size_t, ...) = delete;
于 2016-05-26T22:34:20.093 回答
0

您可以在 Foo 类中声明一个名为“operator new”的函数,该函数将阻止对正常形式的 new 的访问。

这是你想要的那种行为吗?

于 2008-08-14T13:28:17.243 回答
0

您可以将其声明为接口并更直接地从您自己的代码中控制实现类。

于 2008-08-14T13:41:58.543 回答
0

这可以通过将构造函数设为私有并提供静态成员来在堆栈中创建对象来防止

Class Foo
{
    private:
        Foo();
        Foo(Foo& );
    public:
        static Foo GenerateInstance() { 
            Foo a ; return a; 
        }
}

这将使对象的创建始终在堆栈中。

于 2018-01-20T17:52:31.733 回答
-1

不确定这是否提供任何编译时机会,但您是否考虑过为您的类重载“新”运算符?

于 2008-08-14T13:35:29.087 回答