Pregunta

Sé que hacer downcasting así no funcionará. Necesito un método que funcione. Aquí está mi problema: tengo varias clases derivadas diferentes, todas de una clase base. Mi primer intento fue hacer una matriz de clase base. El programa tiene que seleccionar (más o menos al azar) diferentes clases derivadas. Intenté lanzar desde una clase base a la clase derivada, colocándolo en la matriz de la base, pero obviamente eso no funcionó. Esperaba sinceramente otro método que no fuera simplemente pegar matrices de todas las clases derivadas, porque podría haber bastantes clases derivadas. ¿Hay alguna forma mejor de hacer esto en la que simplemente estoy pensando en algo?

Si necesita ejemplos de código o más información, hágamelo saber. Todo tiene sentido para mí, pero es tarde y puede que no tenga sentido para todos los demás, je.

Cualquier ayuda es muy apreciada, chicos.

¿Fue útil?

Solución

No estoy seguro de lo que quieres decir. Parece que usted almacena los objetos por valor y usted tiene una matriz de Base . Eso no funcionará, porque tan pronto como asigne un Derivado, ese objeto se convertirá en una Base, y la parte Derivada del objeto se cortará. Pero creo que quieres tener una serie de punteros para basar:

Base * bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i] = new Derived1;
    else if(r == 1)
        bases[i] = new Derived2;
    // ...
}

Si alguna vez ha trabajado con punteros, sabrá que es un problema manejarlos, pasarlos por alto y no perderlos, ya que tendrá que llamar a eliminarlos para liberar la memoria y llamar al destructor de los objetos. Puede usar shared_ptr, y se lo administrará:

shared_ptr<Base> bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i].reset(new Derived1);
    else if(r == 1)
        bases[i].reset(new Derived2);
    // ...
}

Ahora, puede pasar bases [x] a otro shared_ptr, y notará que tiene más de una referencia: se borrará automáticamente si la última referencia a los objetos desaparece. alcance. Lo ideal es que también sustituyas la matriz en bruto por std :: vector:

std::vector< shared_ptr<Base> > bases;
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases.push_back(shared_ptr<Base>(new Derived1));
    else if(r == 1)
        bases.push_back(shared_ptr<Base>(new Derived2));
    // ...
}

Luego, puedes pasar el vector y no perder el tamaño, y puedes agregarle elementos dinámicamente a pedido. Obtenga el tamaño del vector utilizando bases.size () . Lee sobre shared_ptr aquí .

La conversión de una clase Base a una clase Derivada debe solo realizarse cuando sea absolutamente necesario . Normalmente, desea utilizar una técnica llamada polymorphism , lo que significa que llama a una función en el puntero base, pero en realidad llamará a una función definida en la clase derivada, que tiene la misma firma (nombre y parámetros). son del mismo tipo) y se dice que lo anula . Lea el artículo en wikipedia al respecto. Si realmente necesitas lanzar, puedes hacerlo así para un puntero en bruto:

Derived1 * d = &dynamic_cast<Derived1&>(*bases[x]);

El uso de dynamic_cast garantiza que cuando realiza la conversión al tipo incorrecto (es decir, el tipo que lanza no es el tipo que se creó y asignó al puntero base), obtiene una excepción lanzada por el operador. Para el caso shared_ptr, también hay formas:

shared_ptr<Derived1> d = dynamic_pointer_cast<Derived1>(bases[x]);
if(d) {
    // conversion successful, it pointed to a derived. d and bases[x] point still 
    // to the same object, thus share it. 
}

Otros consejos

La conversión de una clase base a una clase derivada es un mal olor de código en la mayoría de las circunstancias.

Polimorfismo , mediante el uso de métodos virtuales, es un mejor enfoque.

El código de llamada debe invocar un método utilizando el puntero de clase base, permitiendo que se envíe a la implementación adecuada en la clase derivada.

Sé que esta es una respuesta bastante genérica, pero sin algunos fragmentos de código como ejemplos, es difícil recomendar una respuesta más específica en su situación.

Utilice una matriz de BaseClass * , no una matriz de BaseClass . (O elija una biblioteca de punteros inteligentes para administrar la memoria por usted).

Creo que se necesita más información. ¿Por qué estás abatiendo? ¿Estás haciendo lo mismo con cada clase derivada? Si es así, debería utilizar una interfaz o colocar el código en una clase base (o en ambas).

Por ejemplo, si tienes un conjunto de formas (base) y quieres calcular su área, puedes hacer lo siguiente:

    interface IShape
    {
       double GetArea();
    }

    class Square : IShape
    {
       double GetArea()
       {
          return sideLengh*sideLength;
       }
       ...
    }

    class FunnyShape : IShape
    {
       //do funny stuff
       ...
    } 

    ...

void calcTotalArea(List<IShape> shapes)
{
   double total = 0.0;
   foreach (IShape s in shapes)
   {
      total += s.GetArea();
   }
   return total;
}

Todavía no sé lo que estás tratando de hacer. No se pueden abatir objetos en general, sino punteros a objetos. La forma habitual de hacer esto es algo como dynamic_cast (foo), donde foo es de tipo bankAccount *. Esto le dará un puntero a un objeto de MoneyMarket o un puntero nulo.

Sin embargo, la conversión descendente para realizar una operación en un objeto suele ser un error; eso es generalmente mejor hecho por herencia virtual y polimorfismo. Es una forma de clasificar los objetos en diferentes tipos.

Si desea saber cuál es el interés, defina el interés como una función virtual en la cuenta bancaria, y los tipos de cuentas que no pagan intereses solo pueden devolver cero. Si desea saber qué tipos de cuentas son MoneyMarket, es probable que el proceso de reducción de emisiones sea el camino a seguir.

Muy bien, mis disculpas, podría haber publicado algún código y un poco más de información sobre el resultado final deseado.

Aquí está mi ejemplo:

class bankAccount
{
   int money;
   bankAccount() {};

   int MoneyInAccount() {return money;};
}

class savingsAccount : public bankAccount
{
   int SavingsInterest();
}

class MoneyMarket : public bankAccount
{
    int CalculateAllPastReturns();
}

Luego, en mi programa tenía una serie de bankAccount [40] (no era un puntero). Esperaba poder convertir la Cuenta Bancaria en la Cuenta de Ahorros o en el Mercado de Dinero o en la Cuenta de Cheques, etc. Cada una con sus propias clases únicas. A partir de ahí, el programa podría tener una colección de las diferentes cuentas bancarias y su información única.

Por supuesto, sé que tratar de lanzar así es malo, pero no estaba seguro de qué hacer.

Esperaba que simplemente faltara descaradamente algo con punteros, etc. De todos modos, espero que sea un poco más específico. Muchas gracias por la ayuda!

Si tiene una matriz de objetos bankAccount [40], nunca está usando sus clases derivadas.

La conversión a clases derivadas es muy Java-ish porque ahí tienes el operador instanceof y todo es muy natural. En C ++ puedes emularlo con reinterpret_cast (), pero se considera un estilo malo.

¿Qué estás tratando de hacer en primer lugar? Tirar todo en un contenedor?

El problema aquí es que está almacenando objetos reales de BankAccount en su matriz, no objetos de SavingsAccount o MoneyMarket. Encontrarás que lanzar hacia arriba en estos casos no funcionará. Obviamente, puedes intentar usar reinterpret_cast, pero es probable que se rompa. (Imagínese si savingsAccount tuviera una variable de miembro adicional, la tasa de interés, que la haría varios bytes más grande que la clase base)

Lo que debe hacer es almacenar los punteros a los objetos de BankAccount, estos pueden ser de la clase derivada que desee: asigne una cuenta de ahorros y coloque un puntero en su matriz. Eso funciona bien, el compilador se mantiene al tanto del tipo de objeto y ha asignado toda la memoria necesaria para uno de estos tipos derivados.

Ahora, una vez que haya creado correctamente sus cuentas de ahorros o de mercado y almacenadas en algún lugar, con referencias a ellas en su matriz, puede usar dynamic_cast para convertir el puntero contenido en la matriz al tipo real, si intenta convertir una. de estos punteros a un objeto del tipo incorrecto, obtendrás una excepción. (obviamente, no desea almacenar un objeto SavingsAccount y luego acceder a él como si fuera un objeto moneyMarket), solo obtendrá un puntero válido al objeto si el tipo es correcto. Deberá activar RTTI para que esto funcione (es decir, el compilador hará un seguimiento de qué tipo es cada objeto).

La forma alternativa de lograr lo que desea, además de usar una buena unión de estilo C, es colocar todos los métodos a los que accederá en la clase base y llamarlos directamente a los punteros base que ha almacenado en su array.

y después de escribir todo eso, litb dice casi lo mismo, solo que mucho mejor que yo. ¡Dale la garrapata!

Tratar de subir / bajar la cadena de herencia generalmente es simplemente un error. Si está haciendo esto, lo más probable es que no esté utilizando la herencia y el polimorfismo correctamente. Si se da cuenta de que necesita realizar una conversión ascendente o descendente, es probable que tenga un diseño de clase defectuoso.

Como se ha mencionado en otras respuestas, la forma de abordar este tipo de cosas es tener todos los métodos que necesita definidos en la clase base. Si una clase derivada no es compatible con el método, haga que sea un no-op o que devuelva un error.

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