2012-05-11 9 views
7

general, parece preferible en C++ que código que funciones que toman un parámetro a un objeto polimórfico toma un puntero a la misma:¿Por qué los punteros a la clase base son preferibles a las referencias?

class Base {}; 
class DerivedA : public Base {}; 
class DerivedB : public Base {}; 

void OperateOnObject(Base* obj) {}; 

vector<Base*> objects; 
objects.push_back(new DerivedA()); 
objects.push_back(new DerivedB()); 

for (size_t i=0; i<objects.size(); i++) { 
    OperateOnObject(objects[i]); 
} 

Por qué es que OperateOnObject() se escribe generalmente para tomar un puntero a Base en lugar de una ¿referencia? ¿Hay problemas potenciales con el corte? (por ejemplo, el vtable se pierde)

He visto this response a una pregunta similar, pero no parece abordar el mismo problema.

+3

En todos los proyectos para los que trabajé, la preferencia de puntero vs. referencia siempre dependía de algo más que el polimorfismo ... – PlasmaHH

+0

Un factor adicional es que en VisualStudio parece que se obtiene mucha más información de depuración para los punteros (p. Ej. tipo de información para el tipo más derivado) que para las referencias, por lo que también es un factor para preferir uno sobre el otro –

+1

En un buen diseño, las capacidades (in) de un IDE/depurador no deberían influir en las propiedades básicas del diseño. – PlasmaHH

Respuesta

8

Las referencias no presentan problemas con el corte - en este sentido, son tan buenos como los punteros. No hay una razón "difícil" para preferir los punteros, a menos que desee que la semántica de NULL esté disponible para su código (es decir, la capacidad de pasar "nada", que no está allí si utiliza referencias).

Creo que los tipos de punteros se usan a menudo como parámetros de funciones polimórficas para coincidir con la semántica de colecciones: como no se puede hacer una referencia vector, el código terminará teniendo que quitar punteros obtenidos de colecciones antes de pasarlos a funciones . Esto puede ser molesto

+0

Ok, realmente es solo un tema de estilo, no hay una razón técnica para ello. Esto parece estar de acuerdo con la respuesta de Steve –

9

No hay ningún problema con el corte: sus clases no necesitan tablas vtables, ya que no tienen funciones virtuales, pero incluso si las tienen, pass-by-reference no copia el objeto y por lo tanto no funciona cortar cualquier cosa.

Puede hacer llamadas virtuales a través de referencias exactamente como puede a través de punteros. Por lo que sé, no hay ninguna razón práctica, es solo por estilo.

tal vez es que ver con el hecho de que los objetos polimórficos son generalmente creado por puntos con new, y por supuesto debe ser almacenado en contenedores de puntero (o puntero inteligente), ya que no se puede tener un contenedor de referencias. Entonces, puede parecer más consistente manipularlos con el puntero siempre.

Además, si desea utilizar OperateOnObject en un algoritmo estándar como for_each, entonces tiene que aceptar el tipo de elemento del contenedor, que es un puntero, o bien debe envolverlo en un adaptador de función que lo haga la desreferencia. El estándar C++ no tiene ese adaptador, por lo que es un mundo de problemas basar su estilo en él.

relacionadas: mira lo que pasa si OperateOnObject toma una referencia, y que usted repite su vector uso de iteradores:

for (vector<Base*>::iterator i = objects.begin(); i != objects.end(); ++i) { 
    OperateOnObject(**i); 
} 

La 20ª vez que el doble indirección molesta o te confunde, que va a cambiar la firma de OperateOnObject; -)

Como nota aparte, algunas guías de estilo advierten contra pasar referencias no const sin importar si el tipo es una clase base polimórfica, sobre la base de que no se puede decir inmediatamente la diferencia entre pass-by-reference y pass por valor cuando está leyendo código en el sitio de llamadas. Por lo tanto, preferirían OperateOnObject para tomar un puntero de todos modos.

Personalmente, creo que ese argumento es un poco débil: el nombre de una función debe decirlo aproximadamente, y en particular una afirmación como ChangeTheObject(my_object); no es tan sutilmente que me hace alusión que no está utilizando ningún valor de retorno , debe cambiar su argumento. Pero acepto que si sigues un estilo correctamente, hay algún beneficio en distinguir claramente y consistentemente a los mutadores de las funciones puras.

Cuestiones relacionadas