【问题标题】:C++ polymorphism not supported for pointer-to-pointer指针到指针不支持 C++ 多态性
【发布时间】:2009-04-10 07:39:45
【问题描述】:

我正在寻找一种正确的方法来清理我的指针。 这里是示例代码:

class Parent {
   protected:
      int m_Var;
   public:
      Parent() : m_Var(0) {}
      virtual ~Parent() {}
      void PubFunc();
};

class Child : public Parent {
   protected:
      bool m_Bool;
   public:
      Child() : m_Bool(false) {}
      virtual ~Child() {}
      void ChildFunc();
};

void RemoveObj(Parent **ppObj)
{
   *ppObj->PubFunc();
   delete *ppObj;
   ppObj = NULL;
}

int main()
{
   Parent* pPObj = NULL;
   Child*  pCObj = NULL;
   pPObj = new Parent();
   pCObj = new Child();

   RemoveObj(&pPObj);
   RemoveObj(&pCObj); // This is line 33
   return 1;
}

但是编译器报错:

classes.cpp:33: error: invalid conversion from ‘Child**’ to ‘Parent**’
classes.cpp:33: error:   initializing argument 1 of ‘void RemoveObj(Parent**)’

【问题讨论】:

    标签: c++ pointers polymorphism


    【解决方案1】:

    有很多方法可以正确处理内存。

    与您的示例最接近的是:

    template <typename T>
    RemoveObj(T **p)
    {
        if (p == NULL) return;
        delete *p;
        *p = NULL;
    }
    

    此外,您可能还想使用 std::auto_ptr 。它看起来像:

    int main()
    {
       std::auto_ptr<Parent*> pPObj(new Parent);
       std::auto_ptr<Child*> pCObj(new Child);
       // no deletes needed anymore
    

    【讨论】:

    • 关于第一个解决方案:相比于:void RemoveObj(void **ppObj)
    • 第一个建议编译并工作。 void RemoveObj(void **ppObj) 可以定义,但如果你尝试调用它,你的调用不应该编译。
    • @To1ne:我认为删除 void * 实际上是未定义的。使用模板解决方案,您就有了正确的静态类型。应该为 void * 调用什么重要的析构函数?
    • 删除 'void*' 是未定义的
    • 我认为 dynamic_cast 或 reinterpret_cast 也不错?只有当我想调用 *p->PubFunc(); 时才真正需要
    【解决方案2】:

    简单来说:

    Child 是 Parent 的子类,这意味着 Child* 可以替换为 Parent*

    但是

    Child* 不是 Parent* 的子类,因此这意味着 Child** 不能用 Parent** 代替

    “Child”和“Child*”不是同一类型。

    【讨论】:

      【解决方案3】:

      您需要做的是使指向您刚刚删除的对象的所有指针无效。指针的思想是有不止一个指针存储同一个对象的地址。如果不是,则几乎没有理由使用裸指针,因此您尝试捕获的模式不是很有用 - 但您远不是第一个尝试这个的人。正如其他答案所提到的,处理指针的唯一方法是仔细控制对它们的访问。

      你的问题的标题是绝对正确的!这是有充分理由的。指针标识存储特定类型对象的位置。指向指针的指针使您能够更改指针指向的对象。

      void Foo(Parent **pp)
      {
          *pp = new OtherChild();
      }
      

      您的Child 类派生自Parent,我的OtherChild 类也是如此。假设编译器允许你这样做:

      Child *c = 0;
      Foo(&c);
      

      您希望它能够工作,但如果它成功了,那么我们现在将拥有一个 Child 指针 c,它实际上指向 OtherChild 的一个实例。谁说这两种类型兼容?

      同样,这是一个非常常见的误解——它在这里反复出现在其他语言中,尤其是在 C# 中的 List&lt;Parent&gt;List&lt;Child&gt;

      【讨论】:

      • 非常感谢,很好的解释!!
      • 没问题!请记住 - 没有什么比投票更能表达“谢谢”了。
      【解决方案4】:

      删除不需要包装器,保持简单:

      int main()
      {
        Parent* pPObj = NULL;
        Child*  pCObj = NULL;
        pPObj = new Parent();
        pCObj = new Child();
      
        delete pPObj;
        delete pCObj; // This is line 33
        return 1;
      }
      

      请记住,使用 RemoveObj 删除数组类型对象时会遇到问题(因为您始终使用标量 delete)。当然,另一种选择是传递一个标志来表明你想要delete []。但正如我所说:KISS。

      【讨论】:

      • 也许我的例子太简单了...我也想在那个函数中调用 PubFunc()...
      【解决方案5】:

      如果您的问题是处理内存和资源,最好的建议是完全忘记您的方法并使用智能指针。 std::auto_ptrboost::shared_ptr 将是一个起点。

      如果您使用智能指针保存所有堆分配资源,您的代码将更加健壮。

      【讨论】:

        【解决方案6】:

        可以从Item 8. Pointers to Pointers一书中找到一些有用的信息。

        【讨论】:

          【解决方案7】:

          可能是我找到的最简单的解决方案:

          #define __REMOVE_OBJ(pObj) RemoveObj(pObj); pObj = NULL;
          

          就叫这个吧:

             __REMOVE_OBJ(pPObj);
             __REMOVE_OBJ(pCObj);
          

          但我自己不是很喜欢……

          【讨论】:

            【解决方案8】:

            来自make shared_ptr not use delete的讨论

            共享指针将确保您在应该清理的时候进行清理,并且不会访问被破坏的内容。此外,您可以专门化并提供另一种销毁方法。

            boost::shared_ptr<T> ptr( new T, std::mem_fun_ref(&T::deleteMe) );
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-05-07
              • 1970-01-01
              • 2012-04-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-01-25
              相关资源
              最近更新 更多