Pregunta

http://en.wikipedia.org/wiki/Diamond_problem

Sé lo que significa, pero ¿qué pasos puedo tomar para evitarlo?

¿Fue útil?

Solución

Un ejemplo práctico:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

Observe cómo la clase D se hereda de B & amp; C. Pero tanto B & amp; C hereda de A. Esto dará como resultado que se incluyan 2 copias de la clase A en la tabla de visualización.

Para resolver esto, necesitamos herencia virtual. Es la clase A la que necesita ser heredada virtualmente. Por lo tanto, esto solucionará el problema:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

Otros consejos

herencia virtual. Para eso está ahí.

Me limitaría a usar la herencia múltiple de interfaces solamente. Si bien la herencia múltiple de clases es atractiva a veces, también puede ser confusa y dolorosa si confía en ella regularmente.

La herencia es un arma fuerte y fuerte. Úsalo solo cuando realmente lo necesites. En el pasado, la herencia de diamante era una señal de que iba a ir muy lejos con la clasificación, diciendo que un usuario es un " empleado " pero también son un " escucha del widget " ;, pero también son ...

En estos casos, es fácil encontrar varios problemas de herencia.

Los resolví utilizando la composición y los punteros al propietario:

Antes:

class Employee : public WidgetListener, public LectureAttendee
{
public:
     Employee(int x, int y)
         WidgetListener(x), LectureAttendee(y)
     {}
};

Después:

class Employee
{
public:
     Employee(int x, int y)
         : listener(this, x), attendee(this, y)
     {}

     WidgetListener listener;
     LectureAttendee attendee;
};

Sí, los derechos de acceso son diferentes, pero si puede salirse con la suya, sin duplicar el código, es mejor porque es menos poderoso. (Puede ahorrar energía cuando no tenga otra alternativa).

class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

En esto, los atributos de la Clase A se repiten dos veces en la Clase D, lo que hace un mayor uso de la memoria ... Por lo tanto, para ahorrar memoria, creamos un atributo virtual para todos los atributos heredados de la Clase A que se almacenan en un Vtable.

Bueno, lo bueno de Dreaded Diamond es que es un error cuando ocurre. La mejor manera de evitar es averiguar su estructura de herencia de antemano. Por ejemplo, un proyecto en el que trabajo tiene visores y editores. Los editores son subclases lógicas de los espectadores, pero como todos los visores son subclases: TextViewer, ImageViewer, etc., el editor no se deriva de Viewer, lo que permite que las clases de TextEditor y ImageEditor final eviten el diamante.

En los casos en que el diamante no se puede evitar, se utiliza la herencia virtual. La mayor advertencia, sin embargo, con bases virtuales, es que el constructor para la base virtual debe ser llamado por la clase más derivada, lo que significa que una clase que deriva virtualmente no tiene control sobre los parámetros del constructor. Además, la presencia de una base virtual tiende a incurrir en una penalización de rendimiento / espacio en el lanzamiento a través de la cadena, aunque no creo que haya una penalización por más que la primera.

Además, siempre puedes usar el diamante si eres explícito sobre qué base quieres usar. A veces es la única manera.

Sugeriría un mejor diseño de clase. Estoy seguro de que hay algunos problemas que se resuelven mejor a través de la herencia múltiple, pero verifique si hay alguna otra forma primero.

Si no, usa las funciones / interfaces virtuales.

Utilizar herencia por delegación. Luego, ambas clases apuntarán a una base A, pero deben implementar métodos que redirijan a A. Tiene el efecto secundario de convertir a los miembros protegidos de A en " private " miembros en B, C y D, pero ahora no necesita virtual, y no tiene un diamante.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top