2009-05-22 21 views
11

¿Alguien sabe por qué esto da un error de compilación? Probé VS 2005 y Codewarrior:Referencia a punteros y polimorfismo C++

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 dice:

refptr.cpp (33): error C2664: 'RemoveObj': no ​​se puede convertir el parámetro 1 de 'Niño *' para 'padres * &'

Gracias

Respuesta

16

El parámetro ppObj a RemoveOb j es una referencia a un padre *. ¿Qué ocurre si el método RemoveObj() reemplaza el puntero con un puntero a un nuevo objeto Parent? Cuando el método devolvió su pCObjChild* ya no estaría apuntando a un objeto Child.

+0

Buen razonamiento. +1 :) –

4

Desde el C++ estándar (1998)

excepto en el contexto de un inicialización por, una bien formada secuencia conversión (13.3.1.4, 13.3.1.5) definida por el usuario conversión implícita es uno de los formas siguientes: -a secuencia estándar de conversión (13.3.3.1.1), el usuario -a definido ...

13.3.3.1.1

como máximo una conversión de cada categoría está permitido en un solo secuencia de conversión estándar

lo que C++ no puede convertir implícitamente dos veces seguidas: desde un puntero a puntero y luego otra vez desde el apuntador .

Para aclarar esto en cuenta dicha declaración de la RemoveObj

void RemoveObj(Parent ** ppObj) 

y verá este error

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

usted tiene que utilizar la conversión explícita como

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

o tienen para cambiar

void RemoveObj(Parent *& ppObj) 

a

void RemoveObj(Parent * ppObj) 

o para

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

Esto no responde por qué es un error de compilación, pero es lo que debería cambiarse. La referencia al puntero no es necesaria para hacer lo que hace el método. –

+0

Tengo una referencia del sumador al estándar. –

+0

Con la edición, esta es definitivamente una respuesta de por qué está obteniendo un error de compilación. Está intentando una conversión en dos pasos que es ilegal según la parte del estándar @MykolaGolubyev señalada. Desafortunadamente, un 'reinterpret_cast' es la solución más práctica a este problema. –

-2

Esto no es autorizada, pero creo que el problema es la naturaleza polimórfica de clases de C++ no se extiende a sus punteros; lo que espera que se haga aquí es que un Child * sea lanzado a Parent *; aunque puede convertir Child en Parent, no puede convertir el puntero referencia. Es decir, las clases son polimórficas, pero los indicadores de las clases no se toman como referencias. Esto es por la razón que Michael Burr da arriba; el Child * implica una cierta estructura de memoria que el Parent * no contiene.

+3

conversión entre punteros son posibles. la conversión, sin embargo, devuelve un rvalue temporal. que es la razón por la que no se puede pasar a una referencia no constante. –

+1

El problema es de la referencia, no de los punteros. Puede pasar los punteros de manera intercambiable, pero no una referencia a un puntero de un puntero de tipo diferente. –

+2

Creo que estabas hablando de conversiones de lvalues ​​a lvalues ​​w.r.t Child * to Parent *. Tienes razón, esa conversión no es posible. Pero su publicación parece indicar que está insinuando que Child * to Parent * no está permitido (no es así). No perdonó nada, solo te ayudó a descubrir por qué podrían haberte degradado. (si solo le dijeron ...) –

0

ppobj es la referencia para el puntero. * ppobj desreferencia a lo que apunta la variable, de modo que se obtiene la variable del puntero.

Como la desreferencia no es del tipo correcto, está viendo el error.

0

Un puntero a una referencia permite cambiar el valor del puntero en la función. Como se señala en Michael Burr, existe la posibilidad de asignar una referencia de clase incorrecta y devolverla. Imagina todo tu programa usando incorrectamente * pchickens as * peggs :)

Pensé que valía la pena agregar (aunque no explícitamente lo que pediste): Mi preferencia para una implementación polimórfica es mover funciones comunes dentro como métodos. Si todos comparten una función, simplemente agréguela a la clase base.

De cualquier forma, simplemente puede llamar al Foo-> Bar() y lograr el resultado deseado. Pero para el ejemplo de implementación específico que usted da, simplemente elimine Foo llamaría al destructor apropiado.