【问题标题】:Prevent delete on pointer in C++防止在 C++ 中删除指针
【发布时间】:2012-08-08 14:01:36
【问题描述】:

有什么方法可以防止通过声明删除 C++ 中的指针?

我尝试了以下代码但没有运气。

const int* const foo()
{
    static int a;
    return &a;
}

int main()
{
    const int* const a = foo();

    *a = 1;   //compiler error, const int*
    a++;      //compiler error, int* const
    delete a; //no compiler error, I want to have compiler error here

    return 0;
}

【问题讨论】:

  • 好问题,+1,但我很确定,这是不可能的(除非你使用一些包装器/智能指针)
  • @Felics 那么你最终将如何释放内存呢?
  • @PaulManta 我不想释放内存,这就是重点。指针指向一个静态变量。
  • @PaulManta 我不想“解决问题”,我只想知道这是否可行。
  • 没有。返回引用将是一个很好的信号,表明您不希望删除该对象。

标签: c++


【解决方案1】:

您不能以阻止在指针上调用delete 的方式声明指向任意类型的指针。 Deleting a pointer to const (T const*) 解释了为什么会这样。

如果它是一个指向自定义类的指针,您可以将 delete 运算符设为私有:

class C {
    void operator delete( void * ) {}
};

int main() {
    C *c;
    delete c; // Compile error here - C::operator delete is private!
}

您当然不应该将析构函数设为私有(如其他人所建议的那样),因为它也会避免在堆栈上创建对象:

class C {
    ~C() {}
};

int main() {
    C c; // Compile error here - C::~C is private!
}

【讨论】:

  • 受保护的删除在某些情况下也更有意义。
  • 我的想法也是:我们使用类来封装数据(并定义有效的操作)。
  • 在实际释放内存之前覆盖删除全局并执行一些检查怎么样?
  • 您是否也应该将 operator new 设为私有/受保护 - 这样您就不能在堆上泄漏值?
  • @MichaelAnderson:对象不一定会泄漏:对象可以delete this,或者通过其他方法暴露delete 功能(例如,公共release 方法左右)。
【解决方案2】:

简单的答案是。没有办法阻止在指向内置类型的指针上调用 delete。

附录

但是我遇到了类似的情况......我的解决方案是停止使用普通指针,因此不必担心删除。在我的情况下,共享指针是有意义的,但你的唯一指针或类似指针可能就足够了。

//Custom do nothing deleter.
template<typename T> dont_delete( T* ) { /* Do Nothing */ }

shared_ptr<const int> const foo()
{
  static int a;
  return shared_ptr<const int>(&a, &dont_delete<const int> );
}

shared_ptr<const int> const bar()
{
  return shared_ptr<const int>(new int(7) );
}

main()
{
   shared_ptr<const int> p1 = foo();
   shared_ptr<const int> p2 = bar();

   //p1s data _not_ deleted here, 
   //p2s data is deleted here
}

【讨论】:

    【解决方案3】:

    我不完全明白你在问什么。如果您想要一个无法删除的对象,您可以尝试将 foo 设为一个类并将析构函数设为私有。

    class Foo {
    public:
       int a;
    
       Foo(int v) {
           a = b;
       }
    
    private:
       ~Foo() { }
    };
    
    int main() {
    
        Foo *c = new Foo(1);
    
        delete c; // compiler error, ~Foo() is private
    
        return 0;
    }
    

    我将变量“a”设为公开,因为它最初被定义为结构,但您可以(并且应该)将其设为私有,并让访问器强制执行您在原始代码示例中想要的访问规则。

    这不是万无一失的,编译器只会捕获对该类的直接引用。

    【讨论】:

    • 这样你甚至不能在堆栈上创建 Foos。
    【解决方案4】:

    我认为他的意思是意外删除对象(无论是 delete o 还是 free(o)),这可能导致程序崩溃。对于在堆上分配的对象,确实没有办法解决这个问题。就堆栈指针而言,我们都知道它不可能发生。 在顶级类中使用受保护的 dtor 是一种选择,但您必须在子类 dtor 中调用它。

    一个解决方案(即使覆盖删除操作符在表上)是使用一个表映射系统,它返回一个 id/token/what-have-you 但这真的只适用于你正在编写 CSTYLE 代码和编译在 C 约定中。这样做的好处是对用户隐藏对象指针,允许用户传入映射到对象的令牌。这需要工作和经验。

    我什至不会担心它,因为大多数有经验和聪明的程序员都会阅读 API 的文档来避免这种事故。如果是智慧或经验,好吧,那我不说什么了。

    【讨论】:

      【解决方案5】:

      您可以防止在某些类的指针上使用删除运算符。 例如:

      class Object {
      public: void operator delete(void* p) = delete;
      };
      
      class Entity : public Object {    };
      
      int main(int argc, char const *argv[])
      {
          Object* o = new Object;
          Entity* e = new Entity;
      
          delete o; // compiler error
          delete e; // compiler error
      
          return 0;
      }
      

      对于所有继承自 Object 的类都不能删除,因为 Object::operator delete 被删除了。不要将此运算符标记为私有,因为它会在派生或实例化 Object 类时产生编译器错误。请注意,我们仍然可以这样做:

      ::operator delete(o);
      

      这将释放 o 指针但不会调用析构函数。 使用一个类来管理 Object 类的生命周期。一个简单的实现是:

      class Object {
          template<typename type> friend class ptr;
          public: void operator delete(void* p) = delete;
      };
      
      class Entity : public Object { };
      
      template<typename type>
      class Ptr {
      public:
          Ptr(type* obj) : o(obj){}
          ~Ptr() { o->~type(); ::operator delete(o); }
      private: type* o;
      };
      
      int main(int argc, char const *argv[])
      {
          Object* o = new Object;
          Entity* e = new Entity;
      
          // delete o;  // error
          // delete e;  // error
      
          Ptr<Entity> ent = new Entity; // Ptr will delete ent for you.
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-10
        • 1970-01-01
        • 2018-05-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多