Ссылка на указатели и полиморфизм C ++ - PullRequest
11 голосов
/ 22 мая 2009

кто-нибудь знает, почему это дает ошибку компилятора? Я пробовал 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;
}

Визуальная студия говорит:

refptr.cpp (33): ошибка C2664: 'RemoveObj': невозможно преобразовать параметр 1 от 'Child *' до 'Parent * &'

Спасибо

Ответы [ 5 ]

16 голосов
/ 22 мая 2009

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

4 голосов
/ 22 мая 2009

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

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

13.3.3.1.1

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

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

Чтобы прояснить это, рассмотрите такое объявление 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;
}
0 голосов
/ 06 января 2010

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

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

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

0 голосов
/ 22 мая 2009

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

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

0 голосов
/ 22 мая 2009

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

...