C ++ полиморфизм не поддерживается для указателя на указатель - PullRequest
2 голосов
/ 10 апреля 2009

Я ищу правильный способ очистки моих указателей. Вот пример кода:

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**)’

Ответы [ 8 ]

12 голосов
/ 10 апреля 2009

Есть много способов правильно обращаться с памятью.

Примером, близким к вашему примеру, будет:

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
3 голосов
/ 10 апреля 2009

Что вам нужно сделать, это обнулить все указатели на объект, который вы только что удалили. Идея указателей состоит в том, что будет несколько указателей, хранящих адрес одного и того же объекта. Если нет, то есть небольшая причина использовать пустой указатель, поэтому шаблон, который вы пытаетесь захватить, не очень полезен - но вы далеко не первый, кто попробует это сделать. Как уже упоминалось в других ответах, единственный способ справиться с указателями - тщательно контролировать доступ к ним.

Название вашего вопроса абсолютно правильно! Для этого есть веская причина. Указатель идентифицирует местоположение, в котором хранится объект определенного типа. Указатель на указатель дает вам возможность изменить объект, на который указывает указатель.

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

Ваш Child класс является производным от Parent, как и мой OtherChild класс. Предположим, что компилятор позволил вам сделать это:

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

Вы ожидали, что это сработает, но если бы это было так, то теперь у нас был бы Child указатель c, который фактически указывает на экземпляр OtherChild. Кто сказал, что эти два типа совместимы?

Опять же, это очень частое недоразумение - оно часто встречается здесь для других языков, особенно в отношении List<Parent> и List<Child> в C #.

3 голосов
/ 10 апреля 2009

Проще говоря:

Child - это подкласс Parent, что означает, что Child * может быть заменен на Parent *

НО

Child * НЕ является подклассом Parent *, поэтому это означает, что Child ** не может быть заменен на Parent **

«Ребенок» и «Ребенок *» не являются одинаковыми типами.

2 голосов
/ 10 апреля 2009

Если ваша проблема связана с памятью и ресурсами, лучший совет - полностью забыть о вашем подходе и использовать умные указатели. std :: auto_ptr или boost :: shared_ptr будет отправной точкой.

Если вы будете использовать все выделенные для кучи ресурсы с помощью интеллектуальных указателей, ваш код будет более надежным.

2 голосов
/ 10 апреля 2009

Вам не нужна оболочка для удаления, сделайте это просто:

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 []. Но как я уже сказал: ПОЦЕЛУЙ.

1 голос
/ 10 апреля 2009

Вы можете найти некоторую полезную информацию из книги <Общие знания C ++>, пункт 8. Указатели на указатели.

0 голосов
/ 10 апреля 2009

Из обсуждения на сделать так, чтобы shared_ptr не использовал delete

Общий указатель обеспечит вам очистку, когда вы должны, и что у вас нет доступа к тому, что уничтожено. Далее вы можете специализироваться и предоставить альтернативный метод уничтожения.

boost::shared_ptr<T> ptr( new T, std::mem_fun_ref(&T::deleteMe) );
0 голосов
/ 10 апреля 2009

Пожалуй, самое простое решение, которое я нашел:

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

И просто назовите это:

   __REMOVE_OBJ(pPObj);
   __REMOVE_OBJ(pCObj);

Но мне самому не очень нравится ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...