[Consulta] Vector de objectos

Iniciado por bemone, 8 Agosto 2013, 23:35 PM

0 Miembros y 1 Visitante están viendo este tema.

bemone

Buenas, hoy ando con la duda de como llenar el vector con instancias de un objecto en este caso el objeto Cartones.

La clase es esta:

Código (cpp) [Seleccionar]
#ifndef CARTONES_H_INCLUDED
#define CARTONES_H_INCLUDED

#include <vector.h>
#include <stdlib.h>
#include <fstream.h>

class Cartones{
    private:
        fstream fArchivo;
        char *cNumeros;
        int iRangoInicio, iRangoFinal;
        int iCantNumeros;
        int iNumAcertadosMin, iNumAcertadosMax;
        int iNumSerie;
        int iCantNumerosAcertados;

    public:
        Cartones(int iRangoInicio, int iRangoFinal, int iCantNumeros);
        ~Cartones();
        void generarNumerosDeCarton();
        void guardarCartonAlArchivo(fstream &archivo);
        void leerCartonDesdeArchivo(fstream &archivo);
        int compararCartones(Cartones carton);

        // Getters & Setters
        void setNumSerie(int iNumSerie);
        void setCantNumerosAcertados(int iCantNumerosAcertados);
        char* getNumeros();
        int getCantNumerosAcertados();
        int getNumSerie();
        int getCantNumeros();
};

Cartones::Cartones(int iRangoInicio, int iRangoFinal, int iCantNumeros){
    this->iRangoInicio = iRangoInicio;
    this->iRangoFinal = iRangoFinal;
    this->iCantNumeros = iCantNumeros;
    this->cNumeros = new char[iRangoFinal];
}

Cartones::~Cartones(){
    delete[](this->cNumeros);
    this->cNumeros = NULL;
}

void Cartones::setNumSerie(int iNumSerie){this->iNumSerie = iNumSerie;}
void Cartones::setCantNumerosAcertados(int iCantNumerosAcertados){this->iCantNumerosAcertados = iCantNumerosAcertados;}
char* Cartones::getNumeros(){return this->cNumeros;}
int Cartones::getNumSerie(){return this->iNumSerie;}
int Cartones::getCantNumeros(){return this->iCantNumeros;}
int Cartones::getCantNumerosAcertados(){return this->iCantNumerosAcertados;}

void Cartones::generarNumerosDeCarton(){
    int i = 0, aux = 0;
    while(i < this->iCantNumeros){
        aux = random(this->iRangoFinal);
        if(aux < this->iRangoFinal && this->cNumeros[aux] != 'X'){
            this->cNumeros[aux] = 'X';
            i++;
        }
    }
}

int Cartones::compararCartones(Cartones carton){
    int coincidencias = 0;
    for(int i=0; i<iRangoFinal; i++){
        if(this->cNumeros[i] == 'X' && carton.cNumeros[i] == 'X'){
            carton.cNumeros[i] = 'A';
            coincidencias++;
        }
    }
        return coincidencias;
}

void Cartones::leerCartonDesdeArchivo(fstream &archivo){
    int serie, aux;
    if(archivo.is_open() && archivo.gcount() > this->iCantNumeros){
        archivo >> serie;
        this->setNumSerie(serie);
        for(int i=0; i<this->iCantNumeros; i++){
            archivo >> aux;
            this->cNumeros[aux-1] = 'A';
        }
    }
}

void Cartones::guardarCartonAlArchivo(fstream &archivo){
    if(archivo.is_open()){
        archivo << this->iNumSerie << endl;
        for(int i=0; i<this->iRangoFinal; i++)
            if(this->cNumeros[i] == 'A')
                archivo << i+1 << " ";
    }
}

#endif // CARTONES_H_INCLUDED


¿Como es la manera correcta de llenar el vector con las instancias?
Asi lo tengo declarado actualmente:

Código (cpp) [Seleccionar]
int cantCartones = Edit1->Text.ToInt();
    vector<Cartones> vCartones;

    fioArchivo.open("cartones.in");
    for(int i=0; i<cantCartones; i++){
        Cartones cCarton(1, 25, 15);
        cCarton.setNumSerie(i);
        cCarton.generarNumerosDeCarton();
        cCarton.guardarCartonAlArchivo(fioArchivo);
        vCartones.push_back(cCarton);
    }
    fioArchivo.close();


y me esta tirando el siguiente error:
Citar[C++ Error] _construct.h(85): E2285 Could not find a match for 'Cartones::Cartones(const Cartones)'
Odio los tipos de variable de Windows.

eferion

Hay dos formas de hacer lo que dices.

La primera es la que tú has propuesto. En este caso llenas el vector de copias literales de los objetos. La segunda opción es crear objetos dinámicos y llenar el vector con punteros a las instancias.

La primera opción tiene la ventaja de que no te tienes que preocupar por la memoria, pues cuando se destruye el vector la memoria reservada por todos los objetos que contiene se libera igualmente.

Con la segunda opción tienes que borrar tu los objetos manualmente si no quieres dejar lagunas de memoria, sin embargo te aporta mucha más flexibilidad en el diseño.

Con respecto al error que comentas, te está diciendo que no has sobrecargado el constructor copia, que es el que necesita para poder hacer las copias de los objetos en el vector.

Lo que pasa, a diferencia de lo que te dice el mensaje, yo el constructor copia lo implementaría así:

Código (cpp) [Seleccionar]
Cartones( const Cartones& original );

La gracia de ponerle en '&' es que el código resultante es mucho más óptimo ya que evitas crear objetos innecesarios. Esto mismo es aplicable a 'compararCartones'.

Lo de ponerle el const es obvio, el argumento no va a sufrir cambios y es lógico, dado que es una referencia, marcarlo como constante para que quede claro que ese objeto no piensas modificarlo.

Esta misma lógica del const la puedes aplicar a todas las funciones que no modifican el estado de la clase ( se entiende por esto que no cambia absolutamente nada dentro de la clase, sus miembros, durante la ejecución de la función ):

Código (cpp) [Seleccionar]

class Cartones{
    private:
        fstream fArchivo;
        char *cNumeros;
        int iRangoInicio, iRangoFinal;
        int iCantNumeros;
        int iNumAcertadosMin, iNumAcertadosMax;
        int iNumSerie;
        int iCantNumerosAcertados;

    public:
        Cartones(int iRangoInicio, int iRangoFinal, int iCantNumeros);
        ~Cartones();
        void generarNumerosDeCarton();
        void guardarCartonAlArchivo(fstream &archivo);
        void leerCartonDesdeArchivo(fstream &archivo);
        int compararCartones( const Cartones& carton) const;

        // Getters & Setters
        void setNumSerie(int iNumSerie);
        void setCantNumerosAcertados(int iCantNumerosAcertados);
        const char* getNumeros() const;
        int getCantNumerosAcertados() const;
        int getNumSerie() const;
        int getCantNumeros() const;
};


De esta forma, si por error intentas modificar algún miembro de la clase desde dentro de una de estas funciones, el compilador te va a avisar con un error. Son mecanismos para evitar errores tontos de programación.

Además de eso... no entiendo por qué los números los almacenas en un char... creo que es mejor que uses int para ello, es un uso más natural. Y ya puestos, dado que estás en c++, casi sería más lógico que en vez de devolver un vector puro de ints ( int* ), devuelvas un vector c++ de ints ( vector< int > )... Creo que andar manejando memoria a pelo en c++ es generalmente es una mala práctica. Además haciendo esto te evitas el uso de la variable 'icantNumeros'. O incluso, para ser más coherentes con tu diseño, lo suyo sería un vector de booleanos ( vector< bool > ) y marcar con true aquellos números que hayan salido.

Realmente lo suyo sería quizás que almacenases en un pequeño vector los números que juega el cartón en vez de generar todo el cartón y marcar los números jugados... es un pequeño derroche de memoria que no te aporta nada. Usar un vector de números jugados aparte te va a simplificar el uso y gestión de la información.

Otro detalle que veo extraño es la función de compararCartones... con eso entiendo que el int indicará si los cartones son iguales o no. Para realizar esa tarea c++ dispone, en primer lugar, del tipo booleano ( bool para los amigos ). En c es muy habitual devolver un int que indique una igualdad... en c++ es más propio usar bool y que directamente retorne true o false. Además, las labores de comparación es mucho más habitual que se realicen a partir de la sobrecarga de operadores.

Un ejemplo:

Código (cpp) [Seleccionar]

class Cartones{
    public:

        // opcion fea
        int compararCartones( const Cartones& carton) const;

        // opcion ideal
        bool operator==( const Cartones& carton ) const;
        bool operator!=( const Cartones& carton ) const;
};


Además con esto consigues que el código quede más natural.

No es lo mismo poner

Código (cpp) [Seleccionar]

Cartones carton1, carton2;
// ...
if ( carton1 == carton2 )
// ...


que poner

Código (cpp) [Seleccionar]

Cartones carton1, carton2;
// ...
if ( carton1.compararCartones( carton2 ) == X ) // entiéndase X como la cantidad de numeros del carton ... segun tu implementacion ... un poco raro
// ...


Además, en la implementación de esta función modificas la clase que le pasas como parámetro... siento decirte que esos cambios son inútiles porque estás trabajando con una copia de la clase que se destruirá en cuanto salgas de esta función.