Ссылка на указатели и полиморфизм С++

Кто-нибудь знает, почему это дает ошибку компилятора? Я пробовал VS 2005 и Codewarrior:

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)
{
   delete ppObj;
   ppObj = 0;
}

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

   RemoveObj(pPObj);
   RemoveObj(pCObj);
   return 1;
}

Visual studio говорит:

refptr.cpp(33): ошибка C2664: 'RemoveObj': невозможно преобразовать параметр 1 от "Ребенок *" до "Родительский" и "

Спасибо

Ответ 1

Параметр ppObj для RemoveObj является ссылкой на родительский *. Что, если метод RemoveObj() заменил указатель указателем на новый объект Parent? Когда метод вернет ваш, pCObj Child* больше не будет указывать на объект Child.

Ответ 2

Из стандарта С++ (1998)

За исключением контекста инициализация с помощью определяемых пользователем (13.3.1.4, 13.3.1.5), a хорошо сформированное неявное преобразование последовательность является одной из следующих формы: стандартное преобразование последовательность (13.3.3.1.1), пользователь определяется...

13.3.3.1.1

Не более одного обращения от каждого категория допускается в одном стандартная последовательность преобразования

Таким образом, С++ не может преобразовать неявно два раза подряд: от указателя к указателю, а затем снова от указателя.

Чтобы очистить это, рассмотрите такое объявление RemoveObj

void RemoveObj(Parent ** ppObj)

И вы увидите эту ошибку

error: invalid conversion from 'Child**' to 'Parent**'

Вы должны использовать явное преобразование, например

   RemoveObj((Parent**)&pCObj);
   RemoveObj((Parent*&)&pCObj);

или изменить

void RemoveObj(Parent *& ppObj)

к

void RemoveObj(Parent * ppObj)

или

template <typename T>
void RemoveObj(T *& pObj)
{
   delete pObj;
   pObj = 0;
}

Ответ 3

ppobj - это ссылка на указатель. * ppobj разделяет то, что указывает переменная, поэтому вы получаете переменную указателя.

Поскольку разыменование не соответствует правильному типу, вы видите ошибку.

Ответ 4

Указатель на ссылку позволяет изменять значение указателя в функции. Как отметил Michael Burr, будет возможность назначить неверную ссылку на класс и вернуть ее. Представьте себе всю вашу программу неправильно, используя * pchickens as * peggs:)

Я думал, что стоит добавить (хотя явно не то, что вы просили): Мое предпочтение полиморфной реализации состоит в том, чтобы перемещать общие функции внутри как методы. Если все они используют функцию, просто добавьте ее в базовый класс.

Тогда в любом случае вы могли бы просто позвонить Foo- > Bar() и добиться желаемого результата. Но для конкретного примера реализации, который вы даете, просто удалить Foo вызовет соответствующий деструктор.

Ответ 5

Это не авторитетно, но я считаю, что проблема заключается в том, что полиморфная природа классов С++ не распространяется на их указатели; то, что вы ожидаете сделать здесь, заключается в том, чтобы Child * был добавлен к Parent *; в то время как вы можете отличить Child до Parent, вы не можете использовать указатель reference. То есть, классы являются полиморфными, но указатели на классы не используются в качестве ссылок. Это по той причине, что Майкл Берр дает выше; Child * подразумевает определенную структуру памяти, которую не имеет Parent *.