如何防止在堆上创建对象?

问题描述 投票:0回答:9

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

Foo *ptr = new Foo;

并且只允许他们这样做:

Foo myfooObject;

有人有什么想法吗?

干杯,

c++ stack heap-memory
9个回答
29
投票

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

    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

或者,为了在 C++11 及更高版本中获得更好的编译器输出:

    private:
        void* operator new(size_t) = delete;   // delete standard new
        void* operator new[](size_t) = delete; // delete array new

        // explicitly deleting placement new, per the prior example,
        // does not seem to be necessary in modern compilers, but you
        // can if you find you need to
        void* operator new(size_t, void*) = delete;
        void* operator new[](size_t, void*) = delete; 

(良好的编码实践建议您“应该”也重载删除和删除[]运算符——我会的,但由于它们不会被调用,所以没有真的必要。)

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


11
投票

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

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

PS。是的,我知道这可以很容易地规避。我真的不推荐它 - 我认为这是一个坏主意 - 我只是回答问题! ;-)


8
投票

我不知道如何可靠地以便携的方式做到这一点..但是..

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

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

FooClass::FooClass() {
    char dummy;
    ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
    if (displacement > 10000 || displacement < -10000) {
        throw "Not on the stack - maybe..";
    }
}

4
投票

@尼克

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

例如:

struct MyStruct {
    Foo m_foo;
};

MyStruct* p = new MyStruct();

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


4
投票

因为调试标头可以覆盖操作符新签名,所以最好使用...签名作为完整的补救措施:

private:
void* operator new(size_t, ...) = delete;
void* operator new[](size_t, ...) = delete;

0
投票

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

这是您想要的行为吗?


0
投票

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


0
投票

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

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

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


-1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.