2010-08-16 21 views
10

¿Existe alguna forma (práctica) de eludir el orden de llamada del constructor normal (virtual)?Llamar al constructor sobrecargado de una clase base virtual

Ejemplo:

class A 
{ 
    const int i; 

public: 
    A() 
     : i(0) 
    { cout << "calling A()" << endl; } 

    A(int p) 
     : i(p) 
    { cout << "calling A(int)" << endl; } 
}; 

class B 
    : public virtual A 
{ 
public: 
    B(int i) 
     : A(i) 
    { cout << "calling B(int)" << endl; } 
}; 

class C 
    : public B 
{ 
public: 
    C(int i) 
     : A(i), B(i) 
    { cout << "calling C(int)" << endl; } 
}; 

class D 
    : public C 
{ 
public: 
    D(int i) 
     : /*A(i), */ C(i) 
    { cout << "calling D(int)" << endl; } 
}; 


int main() 
{ 
    D d(42); 
    return 0; 
} 

de salida:

llamante A()
llamando B (int)
llamando C (int)
llamando D (int)

lo que quiero es tener algo como:

llamar A (int)
llamando B (int)
llamando C (int)
llamando D (int)


Como ve, hay una herencia virtual involucrada, que lleva al constructor de D a llamar primero al constructor de A, pero como no se proporciona ningún parámetro, llama a A(). Está el const int i que necesita inicialización, así que tengo un problema.

Lo que me gustaría hacer es ocultar los detalles de herencia de C, es por eso que estoy buscando una forma de evitar llamar a A (i) en la lista de inicialización del constructor de D's (y cada derivada). [edit] En este caso específico, puedo suponer que solo hay clases secundarias de herencia única no-virtuales de C (como D es uno). [/ Editar]

[editar]

clases base virtuales se inicializan antes de cualquier clases base no virtuales se inicializan, por lo que sólo la clase más derivada puede inicializar clases base virtuales. - James McNellis

eso es exactamente el punto, yo No quieren que la clase más derivada para llamar al constructor de la clase base virtual. [/ editar]

Considere la siguiente situación (no representados en el ejemplo de código anterior):

A 
/\ 
B0 B1 
\/
    C 
    | 
    D 

entiendo por qué C tiene que llamar al ctor de A (ambigüedad) cuando instanciar C, pero ¿por qué D tiene que invocarlo al crear instancias de D?

+1

No creo que su ejemplo de código coincida con el resultado que proporciona. ¿Estás seguro de que has creado d con la instrucción "D d"? ? –

+0

sry, olvidé el parámetro ... es D d (42) ahora. Gracias. – dyp

+0

Ok, eso parece más justo :-) ¿Puedo preguntar por qué quieres usar la arquitectura de "diamante temido"? ¿No puedes reorganizar tu código de alguna otra manera? –

Respuesta

5

Desafortunadamente, siempre tendrá que llamar al constructor de las clases base virtuales desde la clase más derivada.

Esto se debe a que está diciendo que la base virtual se comparte entre todas las clases que se derivan de ella para la instancia del objeto. Como un constructor solo puede invocarse una vez para una instancia determinada de un objeto, debe llamar explícitamente al constructor en la clase más derivada porque el compilador no sabe cuántas clases comparten la base virtual (parafraseada (probablemente mal) desde C++ Programming Language 3rd edition, section 15.2.4.1). Esto se debe a que el compilador comenzará desde el constructor de la mayoría de las clases base y funcionará a la clase más derivada. Las clases que heredan de una clase base virtual directamente, por el estándar, no llamarán a su constructor de clases base virtual, por lo que deben llamarse explícitamente.

+0

> Las clases que heredan de una clase base virtual directamente, por la norma, no llamarán a su constructor de clases base vitual, por lo que debe llamarse explícitamente. Eso es correcto para la clase B, y luego, la clase C podría llamarlo. Pero como C no es la clase más derivada, D debe llamarla. ¿Alguna forma de dejar que C lo llame? D se deriva directamente de C con herencia única no virtual. – dyp

+0

@DyP: supongamos que agrego una clase E que se deriva de C y luego hago que D se derive tanto de C como de E (incluso si no hago que C sea virtual), ¿cuál C llama al constructor para A? – diverscuba23

+0

sry para eso, edité la pregunta. Puedo suponer que no hay herencia múltiple de C. – dyp

-1

En parashift C++ - faq-lite esta edición is outlined.

+0

Creo que el autor preguntaba por qué siempre tenía que llamar al constructor de las clases base virtuales de la clase más derivada, y no a un problema particular que estaba teniendo con el ejemplo que dio. El ejemplo fue solo para ilustrar su pregunta con claridad. – diverscuba23

+0

@ diverscuba23: Me llevó un poco buscar incluso para entender cuál era el problema. Ahora lo entiendo, pero no entiendo la situación que lo llevaría a esto. – Shamster

2

entiendo por qué C tiene que llamar a la ctor de A (ambigüedad) cuando instanciate C, pero ¿por qué tiene que D llamarlo cuando instanciando D?

Por la misma razón que C tiene que llamarlo. No es un problema de ambigüedad, es el hecho de que la construcción de A debe ser llamada solo una vez (ya que es una base virtual).

Si esperabas que C pudiera inicializar el constructor de A, ¿qué pasaría si la clase D heredara C y otra clase que finalmente heredara A?

+0

> [...] ¿qué pasaría si la clase D fuera a heredar dos C [...]? ¿No debería haber derivado virtualmente de C? – dyp

+0

@DyP: Sí, punto justo sobre dos C's. Lo he corregido de mi respuesta. El punto sigue en pie, por supuesto, por supuesto. – Troubadour

+0

@DyP: no necesariamente, si C puede admitir tener varias copias de sí mismo en un objeto, no necesita ser virtual. De acuerdo, es realmente raro que alguna vez quisieras una situación como esa, pero es posible hacerlo con el lenguaje. – diverscuba23

0

Tales son las reglas. Existen reglas para anular las funciones virtuales y las reglas para construir subobjetos base virtuales. Aunque ambos son conceptualmente muy similares, siguen reglas completamente diferentes, por una razón: la anulación de una función virtual es explícita. Llamar a un constructor está implícito para el constructor predeterminado.

Las funciones virtuales en las clases base virtuales solo requieren tener un overrider final, un overrider que anula a todos los demás overriders. (Las funciones virtuales en clases base no virtuales no pueden tener dos overriders de modo que uno no anule al otro.)

Pero los constructores de la clase base virtual siempre se llaman desde la clase más derivada, y generalmente en la forma implícita de no se molesta en mencionar la clase base virtual en la lista de ctor-init, ya que la mayoría de las clases diseñadas para ser usadas como clases base virtuales son "interfaces puras" sin miembros de datos y sin inicialización de usuario.

Cuestiones relacionadas