Pregunta

Estoy teniendo problemas para entender cuál es el propósito de la palabra clave virtual en C ++. Sé C y Java muy bien, pero soy nuevo en C ++

De Wikipedia

  

En la programación orientada a objetos, una   función virtual o método virtual es   una función o método cuyo comportamiento   puede ser anulado dentro de una heredera   clase por una función con el mismo   firma.

Sin embargo, puedo reemplazar un método como se ve a continuación sin necesidad de utilizar la palabra clave virtual

#include <iostream>

using namespace std;

class A {
    public:
        int a();
};

int A::a() {
    return 1;   
}

class B : A { 
    public:
        int a();
};

int B::a() {
    return 2;
}

int main() {
    B b;
    cout << b.a() << endl;
    return 0;
}

//output: 2

Como se puede ver a continuación, la función A :: se anula un éxito con B :: sin requerir un virtual

Para agravar mi confusión se esta declaración acerca de destructores virtuales, también de Wikipedia

  

como se ilustra en el siguiente ejemplo,   es importante para la clase de base de un C ++   tener un destructor virtual para asegurar   que el destructor de la mayor parte   clase derivada siempre se llamará.

Así virtual también le indica al compilador para llamar a los destructores de los padres? Esto parece ser muy diferente de mi entendimiento original de virtual como "hacer que la función reemplazable"

¿Fue útil?

Solución

Haga los siguientes cambios y verá por qué:

#include <iostream>

using namespace std;

class A {
    public:
        int a();
};

int A::a() {
    return 1;   
}

class B : public A { // Notice public added here
    public:
        int a();
};

int B::a() {
    return 2;
}

int main() {
    A* b = new B(); // Notice we are using a base class pointer here
    cout << b->a() << endl; // This will print 1 instead of 2
    delete b; // Added delete to free b
    return 0;
}

Ahora, para que funcione como usted pensó:

#include <iostream>

using namespace std;

class A {
    public:
        virtual int a(); // Notice virtual added here
};

int A::a() {
    return 1;   
}

class B : public A { // Notice public added here
    public:
        virtual int a(); // Notice virtual added here, but not necessary in C++
};

int B::a() {
    return 2;
}

int main() {
    A* b = new B(); // Notice we are using a base class pointer here
    cout << b->a() << endl; // This will print 2 as intended
    delete b; // Added delete to free b
    return 0;
}

La nota que usted ha incluido sobre destructores virtuales es exactamente correcto. En su muestra no hay nada que necesita ser limpiado, pero decir que ambos A y B tenían destructores. Si no están marcados virtual, cuál va a ser llamado con el puntero de la clase base? Pista:. Se trabajará exactamente el mismo que el método de un () hizo cuando no estaba marcado virtuales

Otros consejos

Se podría pensar en él como sigue.

Todas las funciones en Java son virtuales. Si usted tiene una clase con una función, y se reemplaza esa función en una clase derivada, se llama, no importa el tipo declarado de la variable que se utiliza para llamar a él.

En C ++, por otro lado, no serán necesariamente llamados.

Si usted tiene una base de clase base y una clase derivada derivada, y ambos tienen una función no virtual en ellos, llamado 'foo', entonces

Base * base;
Derived *derived;

base->foo(); // calls Base::foo
derived->foo(); // calls Derived::foo

Si foo es virtual, entonces tanto la llamada Derivado :: foo.

medios virtuales que el método actual se determina el tiempo de ejecución sobre la base de lo que fue la clase no ejemplarizado qué tipo usted utiliza para declarar la variable. En su caso se trata de una anulación estática que irá por el método definido para la clase B no importa lo que era el tipo real del objeto creado

  

Así virtual también le indica al compilador para llamar a los destructores de los padres? Esto parece ser muy diferente de mi entendimiento original de virtual como "hacer que la función reemplazable"

Su original y su nueva comprensión son tanto mal.

  • Los métodos son (que ellos llaman funciones) siempre reemplazable. No importa si es virtual, puro, no virtual o algo así.
  • destructores de padres son siempre llamada. Al igual que los constructores.

"virtual" no sólo hacen una diferencia si se llama a un método Trough un puntero de tipo baseclass puntero a. Dado que en el ejemplo no utiliza punteros en absoluto, virtuales no hace una diferencia en absoluto.

Si utiliza un a variable de tipo puntero a A, es decir A* a;, no sólo se puede asignar otras variables de tipo puntero a una a ella, sino también variables de tipo puntero a B, porque B se deriva de A.

A* a; 
B* b;

b = new B(); // create a object of type B. 
a = b;       // this is valid code. a has still the type pointer-to-A, 
             // but the value it holds is b, a pointer to a B object.

a.a();       // now here is the difference. If a() is non-virtual, A::a()
             // will be called, because a is of type pointer-to-A. 
             // Whether the object it points to is of type A, B or
             // something entirely different doesn't matter, what gets called
             // is determined during compile time from the type of a.

a.a();       // now if a() is virtual, B::a() will be called, the compiler
             // looks during runtime at the value of a, sees that it points
             // to a B object and uses B::a(). What gets called is determined
             // from the type of the __value__ of a.
  

Como se puede ver a continuación, la función A :: se anula un éxito con B :: sin requerir una virtuales

Se puede, o puede no funcionar. En su ejemplo funciona, pero es porque debe crear y utilizar un objeto B directamente y no a través de puntero a A. Ver C ++ FAQ Lite, 20,3 .

  

Así virtual también le indica al compilador para llamar a los destructores de los padres?

Un destructor virtual es necesaria si se elimina un puntero de clase base que apunta a un objeto de clase derivada, y esperas tanto de base y derivados destructores para correr. Ver C ++ FAQ Lite, 20,7 .

Es necesario el virtual si se utiliza un puntero de clase base como consultutah (y otros mientras estoy escribiendo;)) dice que

.

La falta de virtuals permite ahorrar una comprobación para saber método wich lo necesitan llamar (el uno de la clase de base o de alguna derivada). Sin embargo, en este punto no se preocupe de actuaciones, justo en el comportamiento correcto.

El destructor virtual es particularmente importante, ya que las clases derivadas pueden declarar otras variables en el montón (es decir, usando la palabra clave 'nuevo') y tiene que ser capaz de eliminarlo.

Sin embargo , puede notar, que en C ++, se tiende a utilizar menos derivar que en java por ejemplo (que a menudo se utiliza plantillas para un uso similar), y tal vez ni siquiera necesita que preocuparse por eso. Además, si usted no declara sus objetos en el montón ( "A una;" en lugar de "A * a = new A ();"), entonces usted no tiene que preocuparse por ello tampoco. Por supuesto, esto dependerá en gran medida qué / cómo se desarrolla y si piensa que alguien más va a derivar su clase o no.

Trate ((A *) y b) .a () y ver lo que se llamaba entonces.

La palabra reservada virtual le permite tratar un objeto de una manera abstracta (decir, a través de un puntero de clase base) y aún así llamar a código descendiente ...

Dicho de otra manera, la palabra clave virtual "permite llamada viejo código nuevo código". Es posible que haya escrito el código para funcionar en una de, sino a través de las funciones virtuales, que el código puede llamar de nuevo una B ().

Digamos que ejemplarizado B pero se mantuvo como una instancia de un R:

A *a = new B();

y se llama función a (), cuya puesta en práctica de un () será llamado?

Si un no () se serán llamados virtuales Atléticos. Si una se llamaría () era la versión virtual de la clase sub instancia de un (), independientemente de la forma en que lo está sosteniendo.

Si constructor asignado toneladas de B de memoria para matrices o archivos abiertos, llamando

delete a;

aseguraría destructor de B fue llamado independientemente de cómo se está celebrando, ya sea por una clase base o interfaz o lo que sea.

Buena pregunta por el camino.

Siempre pienso en ello como si fueran piezas de ajedrez (mi primer experimento con OO).

Un tablero de ajedrez mantiene punteros a todas las piezas. cuadrados vacíos son punteros NULL. Pero todo lo que se sabe es que cada puntero señala una pieza de un ajedrez. La junta no necesita saber más información. Pero cuando una pieza se mueve el tablero no sabe que es un movimiento válido, ya que cada pice tiene diferentes characteristica acerca de cómo se mueve. Así que la junta tiene que comprobar con la pieza si el movimiento es válido.

Piece*    board[8][8];

CheckMove(Point const& from,Point const& too)
{
    Piece*  piece = board[from.x][from.y];
    if (piece != NULL)
    {
        if (!piece->checkValidMove(from,too))
        {    throw std::exception("Bad Move");
        }
        // Other checks.
    }
}

class Piece
{
    virtual bool checkValidMove(Point const& from,Point const& too)  = 0;
};

class Queen: public Piece
{
    virtual bool checkValidMove(Point const& from,Point const& too) 
    {
         if (CheckHorizontalMove(from,too) || CheckVerticalMoce(from,too) || CheckDiagonalMove(from,too))
         {
             .....
         }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top