【问题标题】:How to prevent an object being created on the heap?如何防止在堆上创建对象?
【发布时间】:2010-09-05 21:00:31
【问题描述】:

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

Foo *ptr = new Foo;

并且只允许他们这样做:

Foo myfooObject;

有人有什么想法吗?

干杯,

【问题讨论】:

标签: c++ stack heap-memory


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

        • 我认为 this 和 dummy 无论是在堆中还是在栈中,总是会彼此靠近
        • @Vargas - 我不同意。 'dummy' 将始终在堆栈上,因为它是一个自动局部变量。 this 指针可以指向堆栈(如果 FooClass 用作局部变量)或堆(如果 FooClass 在堆上分配,或者在一个类中聚合,然后在堆上分配)。
        • 你是对的,我把 dummy 误认为是一个成员变量......对不起
        【解决方案5】:

        @尼克

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

        例如:

        struct MyStruct {
            Foo m_foo;
        };
        
        MyStruct* p = new MyStruct();
        

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

            Nick's answer 是一个很好的起点,但不完整,因为您实际上需要重载:

            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% 的方法。

            【讨论】:

            • 私有构造函数与公共静态工厂方法(按值返回)相结合是否可以实现相同的效果?
            • @kevinarpe 这取决于我们阅读问题的字面意思。如果您这样做,问题中的确切代码Foo myfooObject; 将无法编译。也就是说,如果我想控制对象的创建方式,我更喜欢你所建议的方法。
            • 请注意,这可以使用::new 而不是new 来规避,因为这将在全局范围内执行operator new 的查找。
            【解决方案8】:

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

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

            【讨论】:

              【解决方案9】:

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

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-10-23
                • 2021-05-20
                • 1970-01-01
                • 1970-01-01
                • 2014-06-01
                • 1970-01-01
                相关资源
                最近更新 更多