2009-04-10 20 views
6

yo estaba tratando de responder a la pregunta mencionó here pasando la referencia al puntero en lugar del puntero a puntero de esta manera:la conversión de * Derivado de Base * y

class Parent 
{ 
}; 

class Child : public Parent 
{ 
}; 

void RemoveObj(Parent*& pObj) 
{ 
    delete pObj; 
    pObj = NULL; 
} 

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



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

Pero esto produce el siguiente error de compilación en línea 32:

C2664 de error: 'RemoveObj': no ​​se puede convertir parámetro 1 de 'Child *' para 'Parent * &'

Acepto que la conversión de Child ** a Parent ** no está permitida. Pero, ¿por qué esta conversión tampoco está permitida?

+0

Por qué return 1; al final de main()? ¿No se supone que es return 0? ? – DeadHead

+0

Acabo de copiar pegado el código de la pregunta vinculada ... pero eso no está relacionado con la pregunta – Naveen

+0

Oh, sé que no estaba realmente relacionado con la pregunta. Mera curiosidad combinada con pensar que podría haber habido alguna brecha en mi conocimiento. – DeadHead

Respuesta

7

un objeto de tipo Child* no se puede enlazar a un Parent*& por exactamente la misma razón que un Child** no se puede convertir en un Parent**. Permitirlo permitiría al programador (intencionalmente o no) romper la seguridad del tipo sin un yeso.

class Animal {}; 

class DangerousShark : public Animal {}; 

class CuteKitten : public Animal {}; 

void f(Animal*& animalPtrRef, Animal* anotherAnimalPtr) 
{ 
    animalPtrRef = anotherAnimalPtr; 
} 

void g() 
{ 
    DangerousShark myPet; 
    CuteKitten* harmlessPetPtr; 

    f(harmlessPetPtr, &myPet); // Fortunately, an illegal function call. 
} 

Editar

creo que parte de la confusión surge debido al uso flexible de las palabras 'convertir' y 'conversión'.

Las referencias no pueden ser rebotadas, a diferencia de los objetos que pueden reasignarse, por lo que en el contexto de las referencias cuando hablamos de conversión solo podemos preocuparnos por la inicialización de una nueva referencia.

Las referencias siempre están ligadas a un objeto, y por la pregunta del OP, estaba claro que intenta obtener una referencia que sea un enlace directo a un objeto existente. Esto solo está permitido si el objeto utilizado para inicializar la referencia es compatible con la referencia con el tipo de la referencia. Básicamente, esto es solo si los tipos son los mismos, o el tipo del objeto se deriva del tipo de la referencia y el tipo de referencia está al menos tan calificado como cv como el objeto de inicialización. En particular, los punteros a diferentes tipos no son compatibles con la referencia, independientemente de la relación de los tipos apuntados.

En otros casos, una referencia se puede inicializar con algo que se puede convertir al tipo de referencia. En estos casos, sin embargo, la referencia debe ser const y no volátil y la conversión creará un temporal y la referencia estará vinculada a este objeto temporal y no al original. Como se señaló, esto no es adecuado para los requisitos del ejemplo motivador de OP.

En resumen, un Child se pueden unir directamente a un Parent& sino un Child* no se pueden unir directamente a un Parent*&. Se puede inicializar un Parent* const& con un Child*, pero la referencia realmente se vinculará a un objeto temporal Parent* copiado-inicializado desde el objeto Child*.

+0

Gracias por el ejemplo, está claro ahora. – Naveen

+0

@Charles Bailey: Bastantes problemas: [1] No se solicitará el dtor correcto para objetos CuteKitten ya que el dtor para Animal no es virtual. [2] inofensivoPtr no está definido. – dirkgently

+0

Gracias, cambié de inofensivo a inofensivo. Empecé en el último momento pero solo en un lugar. No hay eliminación pasando en mi ejemplo, así que no estoy seguro de cómo se aplica tu punto [1]. –

4
  • Sus clases no tienen una función virtual. Ver FAQ 20.7

  • Beacuse Parent *& es una referencia a un puntero a un objeto Parent. Está pasando un puntero a Child; estos son tipos incompatibles. Puede enlazar un temporal a un const-referencia es decir, si cambia de parámetro a:

    void RemoveObj(Parent* const& foo);

Pero entonces usted no será capaz de hacer mucho con esto.

que era sólo un código de prueba por lo que no hice ninguna destructores virtuales. Si entiendo correctamente en la segunda llamada de RemoveObj(), obtengo un objeto Parent * temporal que se puede pasar como una referencia constante a la función. ¿Es esto correcto?

me sugieren fuertemente que ejecute el programa siguiente en el modo estándar de C++ 98, una vez que en y luego otra vez después de haber comentado foo(b) y sin comentar delete b. A continuación, intente poner virtual antes de ~s(). ¡Las diferencias deben ser autoexplicativas!

#include <iostream> 
using namespace std; 
struct s { 
    s() {cout << __func__ << endl; } 
    ~s() {cout << __func__ << endl; } 
}; 

struct t : s { 
    t() {cout << __func__ << endl; } 
    ~t() {cout << __func__ << endl; } 
}; 

void foo(s* const& x) { delete x; } 

int main() { 
t* b = new t; 
foo(b); 
//delete b; 
} 
+0

Era solo un código de prueba, así que no hice destructores virtuales. Si entiendo correctamente en la segunda llamada de RemoveObj(), obtengo un objeto Parent * temporal que se puede pasar como una referencia constante a la función. ¿Es esto correcto? – Naveen

0

tipo * & es otra forma sintáctica de tipo ** * Padres y & y el Niño * & no están relacionados entre sí, así como los padres y el niño ** ** - estos son los diferentes tipos no en una clase jerarquía.

0

Esto no funcionará por las razones mencionadas por Dirk. Si realmente necesita un método RemoveObj entonces yo simplemente guardar su objeto Niño recién asignada como padre *:

#include <iostream> 

class Parent 
{ 
public: 
    virtual ~Parent() 
    { 
     std::cout << "Parent destructor" << std::endl; 
    } 
}; 

class Child : public Parent 
{ 
public: 
    virtual ~Child() 
    { 
     std::cout << "Child destructor" << std::endl; 
    } 
}; 

void RemoveObj(Parent*& pObj) 
{ 
    delete pObj; 
    pObj = NULL; 
} 



int main (int argc, const char * argv[]) { 

    Parent* pPObj = new Parent; 
    Parent* pCObj = new Child; 

    RemoveObj(pPObj);  
    RemoveObj(pCObj); // This is line 32 


    return 0; 
} 
+0

Sí, hacerlo a mano (sin conversión implícita) funciona. –

1

puede convertir un Child* to a Parent*: esto crea un temporal. Pero no puede enlazar una referencia no const a esa temporal.

Eso no es un problema de **/*&/etc. Lo que estás tratando de hacer está totalmente bien y tiene sentido. Ese Shark vs. Kitten tiene el mismo problema: no es cuestión de mezclar gatitos y tiburones. No puede enlazar una referencia no constante con ese puntero sin nombre.

Este no es el problema Parent** vs. Child**: allí, si un Child** fue un Parent**, entonces se podría asignar p[0] = new NotAChild;. Una colección de objetos que son todos subtipos de A no es una colección de A.

Cuestiones relacionadas