【问题标题】:What could this curious combination of "while" and "delete" mean?“while”和“delete”这个奇怪的组合是什么意思?
【发布时间】:2009-12-22 12:29:55
【问题描述】:

回顾一个相当老的项目我发现了以下奇怪的代码sn-p(只提取了相关代码):

class CCuriousClass {
    ~CCuriousClass();
    CSomeType* object;
};

CCuriousClass::~CCuriousClass()
{
    while( object != NULL ) {
        delete object;
    }
}

我是否监督过任何事情,或者这是一条通向未定义行为的简单道路?

我在这里看到的是,如果 object 在被调用的 CCuriousClass::~CCuriousClass() 处是一个空指针,一切都会好起来的 - 不采取任何行动 - 但如果 object 不为空,这将是一个无限循环内部未定义的行为。

这很可能是一个错误或一些我不理解的智能构造吗?

【问题讨论】:

  • 也许其他线程中的对象发生了什么事?
  • 我不知道,但你能重载对象的“删除”运算符吗?
  • @Mike,你可以,但是对象的析构函数仍然会被调用。
  • 看起来不对。即使它有效(由于一些愚蠢的析构函数代码),也应该高度评论以准确描述正在发生的事情。然后当明智的人看到它并读取 cmets 时,他们应该重新分解以使其像普通代码一样工作。

标签: c++ memory-management pointers


【解决方案1】:

这看起来像一个错误。

【讨论】:

    【解决方案2】:

    可能是某个疯子实现了CSomeType,并对其拥有的CCuriousClass 进行了反向引用,并且它的析构函数有时会创建一个替换。像这样的:

    class CSomeType
    {
    public:
        explicit CSomeType(CCuriousClass &parent) : parent(parent) {}
        ~CSomeType()
        {
            parent.object = respawn() ? new CSomeType(parent) : 0;
        }
    private:
        CCuriousClass &parent;
    };
    

    我并不是建议任何人都应该编写这种扭曲的逻辑。它可能仍然会给出未定义的行为,因为我相信 delete 被允许修改指针。但它可以解释为什么有人会认为给定的代码可能是有效的。

    另一方面,这可能只是由于对 delete 工作原理的误解而导致的错误。

    【讨论】:

    • 太美了! :p 从现在开始,我将编写我所有的析构函数。
    • respawn() 当然应该返回一个随机位来表示真正的邪恶
    • delete 不允许修改其操作数(因为它甚至不需要是左值 - 考虑 delete new int),所以没有 U.B.这里。只是纯粹的邪恶。
    【解决方案3】:

    因为您的问题似乎暗示“有人对此有何用意?”而不是“为什么这是一个绝妙的主意?”我建议如下:

    class CSomeType {
        CCuriousClass* m_plistentry;
        CSomeType* m_pnext;
    
        ~CSomeType() {
            m_plistentry->object = m_pnext;
        }
    };
    

    基本思想可能是所有者指向列表的头部,而列表只能在头部删除。如果头被删除,它将其父指针设置为列表的新头。如果父级被破坏,它会破坏每个列表元素。

    现在这显然是来自疯狂小镇的代码。

    【讨论】:

    • 不能改变原代码中被删除的指针。
    • @Neil 我很好奇,你为什么不能这样做?
    • @Neil,是的,我也想知道。这对我来说似乎是一个有效的解释
    • 好吧,我收回它,它可以,当然前提是原始类设置了指针,显然它没有。
    • 这种结构是邪恶的。
    【解决方案4】:

    正如你所说,这是一个错误。 delete 不会将它删除的指针设置为 NULL,因此您所拥有的是一个无限循环,它可能会或可能不会因两次删除同一指针而导致的未定义行为而终止。

    【讨论】:

      【解决方案5】:

      这种行为是可能的,前提是 CSomeType 的实例知道存储指向自身的指针的地址(CSomeType 中的 CSomeType** 成员),以便它可以在删除时重置它。我不知道为什么需要它。

      示例:

      struct self_know{
          self_know** pptr;
          int cnt;
      
          static self_know* create(self_know **_pptr){
              *_pptr = ::new self_know;
              (*_pptr)->cnt = 10;
              (*_pptr)->pptr = _pptr;
              return *_pptr;
          }
      
          void operator delete(void*it){
             self_know *s = (self_know*)it;
             if(--s->cnt<0){
               *(s->pptr)=0;
               ::delete s;
             }
      
          }
      };
      
      #include<iostream>
      main(){
        self_know *p = 0;
        self_know::create(&p);
        while( p != 0){
           std::cout << p->cnt << std::endl;
           delete p;
        }
      }
      

      【讨论】:

        【解决方案6】:

        另一个可能的理论是有人用预处理器玩了一个讨厌的把戏。说:

        struct delete_and_null {
            template<class T>
            delete_and_null& operator, (T*& p) {
              delete p;
              p = 0;
              return *this;
            }
        } delete_and_null;
        
        #define delete delete_and_null,
        

        这并不能解释循环的必要性,但至少它会避免 U.B.并最终终止。

        【讨论】:

          【解决方案7】:

          这似乎是一个错误,除非CSomeType 的析构函数能够以某种方式修改此对象。

          【讨论】:

            【解决方案8】:

            这不仅可能是一个错误,而且绝对是毫无意义的检查,因为删除空指针很好。替换为

            CCuriousClass::~CCuriousClass()
            {    
                delete object;    
            }
            

            或者更好的是使用智能指针并完全摆脱析构函数。

            【讨论】:

              【解决方案9】:

              查看 CSomeType 类定义。可能有一个“!=”函数重载。否则这显然是一个错误。

              【讨论】:

                【解决方案10】:

                代码似乎有问题。 确保析构函数不应该抛出任何异常否则它可能会终止程序,它应该是这样的。

                CCuriousClass::~CCuriousClass()
                {
                    try
                    {    
                          if( object != NULL ) 
                          {
                              delete object;
                              object = NULL;
                          }    
                    }
                    catch(...) 
                    {  }
                }
                

                【讨论】:

                  【解决方案11】:

                  我怀疑它来自一个不了解析构函数和/或运算符删除的疯狂 C 程序员。要么是那个人,要么是笨拙地错误输入了“while”而不是“if”并且不知道 delete 已经检查了 null 的人。

                  我不会浪费时间去猜测疯狂的代码。重要的是要认识到这是愚蠢和疯狂的。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-04-28
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-09-25
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多