2012-02-02 21 views
8
class base { 
public: 
    void virtual fn(int i) { 
     cout << "base" << endl; 
    } 
}; 

class der : public base{ 
    public: 
    void fn(char i) { 
     cout << "der" << endl; 
    } 
}; 

int main() { 

    base* p = new der; 
    char i = 5; 
    p->fn(i); 
    cout << sizeof(base); 
    return 0; 
} 

aquí Firma del fn función definida en base clase es diferente de la firma de la función fn() definido en la clase der embargo nombre de la función es la misma. Por lo tanto, la función definida en der clase oculta base clase función fn(). Entonces la clase der versión de fn no puede ser llamada por p->fn(i) llamada; Está bien.¿Por qué se requiere un vptr cuando la clase derivada no anula la función virtual?

Mi punto es entonces ¿por qué sizeof clase base o der es 4 si no hay uso de VTABLE puntero? ¿Cuál es el requisito de puntero VTABLE aquí?

+0

¿Alguna vez oíste sobre la sobrecarga en C++? –

+0

@KamilKlimek: la sobrecarga es el acto de declarar múltiples funciones con diferentes firmas. Lo que probablemente quiera decir es que está anulando (lo que significa que está reimplementando métodos en clases derivadas). –

+0

para su información amable, estos no son función de sobrecarga. – user966379

Respuesta

6

Tenga en cuenta que esto depende en gran medida de la implementación & puede variar para cada compilador.

El requisito para la presencia de vtable es que la clase Base está pensada para herencia y extensión, y una clase derivada de ella puede anular el método.

Las dos clases Base y Derived pueden residir en diferentes Unidades de traducción y el compilador al compilar la clase Base no sabrá realmente si el método será invalidado o no. Entonces, si encuentra la palabra clave virtual, genera el vtable.

+1

Pero en este ejemplo, todo el código * está * en el mismo módulo y el compilador podría saberlo. –

0

Herencia es una relación is-a. der es-a base. base tiene tamaño 4, der tendrá al menos el tamaño 4. vftableptr es miembro de base, será miembro de der.

base tiene un método virtual, por lo que tendrá un puntero a la tabla virtual, independientemente de si la usa o no.

+0

Se está perdiendo el punto, la Q OP pregunta es * ¿por qué el tamaño de la clase base es '4'? * Y no * ¿Por qué el tamaño de la clase derivada es' 4'? * –

+0

si ninguna de las clases derivadas anula alguna vez el virtual fucntn –

+0

@Als Estaba confundido porque me parece bastante sencillo. Tiene un método virtual, ¿por qué no debería tener un puntero al vtable? Edité mi respuesta para reflejar esto. –

1

El vtable generalmente no solo se usa para funciones virtuales, sino que también se usa para identificar el tipo de clase cuando se hace dynamic_cast o cuando el programa accede al type_info para la clase.

Si el compilador detecta que no hay funciones virtuales son cada vez anulados y no se utiliza ninguna de las otras características, que sólo podría eliminar el puntero vtable como una optimización.

Obviamente, el escritor del compilador no ha encontrado que valga la pena hacerlo. Probablemente porque no se usará con mucha frecuencia, y porque puede hacerlo usted mismo eliminando el virtual de la clase base.

+0

A virtual b (int), B: public A - no override, C: public B override b (int), D: public C - no override. ¿Y cómo se exceptúa el compilador para comportarse en este caso? ¿Dónde debe ser vtable y dónde no? si I B * instancia = nueva D; ? –

+0

¿Qué sucede si se usa como fábrica de complementos? No tiene forma física de saber si habrá o no clase heredada que anule el método virtual. –

+0

Si hay una anulación en alguna parte, el vtable tendrá que estar allí en todas partes, porque solo puede haber una versión de cada clase. Es solo si (¡grande si!) El compilador puede decir con certeza que no es necesario, que puede optimizarse. –

1

El compilador no puede optimizar a cabo vtable variable miembro de la clase 'base', ya que podría haber otro archivo de origen en el mismo u otro proyecto que contendrá lo siguiente:

struct ived : base { 
    ived() : p(new char[BIG_DATA_SIZE]) {} 
    virtual ~ived(); 
    virtual void fn(int); 
private: 
    char* p; 
}; 

El destructor y fn podía implementarse en otro lugar:

ived::~ived() { delete[] p; } 

void ived::fn(int) { 
    cout << "ived" << endl; 
} 

Y en algún otro lugar que podría haber un código como éste:

base* object = new ived; 
ived->fn(0); 
delete object; 
cout << sizeof(base) << endl; 

Entonces, habría dos problemas: no se llamó a la función virtual ived::fn, no se llamó al destructor virtual, por lo que BIG_DATA_SIZE no se eliminó. De lo contrario, sizeof(base) aquí sería diferente. Es por eso que los compiladores siempre generan vtable para cualquier clase con una función miembro virtual o una clase base virtual.

En cuanto a la invocación de destructores en las clases derivadas, debe considerarse como una obligación: si tiene cualquier clase con cualquier función virtual, esa clase también declarará un destructor virtual.

Cuestiones relacionadas