1

Use la getline en mi código para que pudiera guardar los espacios cuando escribo un titulo largo y cambiando el tipo de dato string a char , pero a la hora de volver a agregarle mi menú falla, lo he tratado de resolver pero no logro encontrar solución del porque falla. Al momento de capturar una película y que me regrese al menú es cuando se traba y me aparece "opción no valida".

#include <iostream>
#include <string>
#include<windows.h>
using namespace std;

class PELICULAS {

public:
    char titulo[100];
    char clasificacion[100];
    char duracion[100];
    char horario[100];
    char costo[100];
    char formato[100];

void mostrar ();
void capturar ();

}da;

void PELICULAS :: capturar(){
    cout<< "DATOS DE LAS PELICULAS"<< endl<<endl;
    cout<< "Titulo de la pelicula: "<<endl; 
    cin.getline(da.titulo,100);
    cin.ignore();
    cout<< "Clasificacion: "<<endl;
    cin.getline(da.clasificacion,100);
    cin.ignore();
    cout<< "Duracion: "<<endl;
    cin.getline(da.duracion,100);
    cin.ignore();
    cout<<"Horario: "<<endl;
    cin.getline(da.horario,100);
    cin.ignore();
    cout<<"Costo: "<<endl;
    cin.getline(da.costo,100);
    cin.ignore();
    cout<<"Formato: "<<endl;
    cin.getline(da.formato,100);
    cin.ignore();
};

void PELICULAS::mostrar(){
   cout<<"PELICULA EXISTENTE"<<endl<<endl;
   cout<<da.titulo<< endl;
   cout<< da.clasificacion<< endl;
   cout<< da.duracion<<endl;
   cout<<da.horario<<endl;
   cout<<da.costo<<endl;
   cout<<da.formato<<endl;
};

int main(){

    int opcion, bucle;
    bucle =1;
    do
        {
        system("cls");
        cout<<"MENU PELICULAS"<<endl<<endl;
        cout<<"1. Capturar"<<endl;
        cout<<"2. Mostrar"<<endl;
        cout<<"3. Salir"<<endl;
        cout<<"ELIGE UNA OPCION: "; cin>>opcion;
        switch(opcion)
        {
        case 1:
           {
                system ("cls"); 
                PELICULAS datos;
                datos.capturar();
                system("pause");
                break;
            }
        case 2:
            {
                system ("cls");
                PELICULAS datos1;
                datos1.mostrar();
                system("pause");
                break;  
            }
        case 3:
            {
                bucle +=1;
                cout<<endl<<"G R A C I A S"<<endl;
                system("pause");
                break;
            }
        default:
            {
                cout<<"O P C I O N  N O  V A L I D A"<<endl;
            }
    }
}
while (bucle == 1);
return 0;
}

introducir la descripción de la imagen aquí

eferion
  • 49,291
  • 5
  • 30
  • 72
  • ¿Qué significa que al volver a agregarle el menú falla? ¿Qué fallo? ¿Sale un error por pantalla? ¿Aparecen datos que no te esperas? ¿Se para el programa? Por favor, se más específico en tus detalles **ayúdanos a ayudarte**. – PaperBirdMaster Feb 28 '19 at 06:50

3 Answers3

2

Tu código tiene varios problemas:

Error 1

Las funciones no terminan con punto y coma:

void PELICULAS::mostrar(){
   cout<<"PELICULA EXISTENTE"<<endl<<endl;
   cout<<da.titulo<< endl;
   cout<< da.clasificacion<< endl;
   cout<< da.duracion<<endl;
   cout<<da.horario<<endl;
   cout<<da.costo<<endl;
   cout<<da.formato<<endl;
}; // <<--- ese punto y coma sobra

Error 2

Estás mezclando conceptos a la hora de implementar la clase. Las funciones miembro de una clase tienen un puntero implícito this que apunta al objeto sobre el que se está invocando la función. Tu usas da así que sobreescribiras todo el dato el mismo objeto.

El uso del puntero this es opcional. Si no se indica el compilador lo añadirá automáticamente:

class PELICULAS {
  // ...
}/*da*/; // <<--- Ese da sobra

void PELICULAS::mostrar(){
   cout<<"PELICULA EXISTENTE"<<endl<<endl;
   cout<<titulo<< endl;              // <<--- sin this
   cout<<this->clasificacion<< endl; // <<--- con this
   cout<</*da.*/duracion<<endl;      // <<--- pero no 'da'
   cout<</*da.*/horario<<endl;
   cout<</*da.*/costo<<endl;
   cout<</*da.*/formato<<endl;
};

Error 3

El orden de ignore y de getline debe ser justo el opuesto al que estás usando.

La instrucción cin>>opcion deja el salto de línea en el buffer de entrada, luego la primera instrucción getline leera ese salto de línea y no hará nada más.

Además, getline sí que descarta el salto de línea con el que concluye la lectura, luego el ignore que sigue a cada getline va a descartar el primer caracter del siguiente campo.

Es decir, antes del primer getline debes usar ignore y después de eso debes borrar el resto de ignore del código.

Error 4

La opción 2 del menú no va a funcionar jamás ya que estás creando todo el rato objetos temporales:

case 1:
{
    system ("cls"); 
    PELICULAS datos; // <<--- objeto temporal
    datos.capturar();
    system("pause");
    break;
}
case 2:
{
    system ("cls");
    PELICULAS datos1; // <<--- objeto temporal
    datos1.mostrar();
    system("pause");
    break;  
}

Estos dos objetos, al llegar al break correspondiente, desaparecerán y perderás los datos que tuviesen almacenados.

Tienes que usar un array de objetos o, si solo te interesa una, el objeto debe estar declarado a nivel del main, no dentro de un case:

int main(){

    PELICULAS datos; // <<--- objeto comun
    do
    {
        // ...

        switch(opcion)
        {
            case 1:
            {
                system ("cls"); 
                datos.capturar(); // <<--- usamos objeto comun
                system("pause");
                break;
            } 
            case 2:
            {
                system ("cls");
                datos.mostrar(); // <<--- usamos objeto comun
                system("pause");
                break;  
            }

Sugerencia 1

Importas la cabecera windows.h, la cual no usas en ningún momento. No conviene importar librerías que no se usan ya que puedes traerte funciones que no esperas y eso forzar a que tu programa se comporte de forma rara (por ejemplo un operador de conversión).

Sugerencia 2

Las clases nacieron con la idea de encapsular el estado de los objetos y dificultar que se usen de forma incorrecta. Si dejas todas las variables públicas no estás aprovechando esta característica de la progamación orientada a objetos.

Además fíjate que tu código no necesita acceder a esas variables fuera de la clase. Te sugiero declarar las variables como privadas:

class PELICULAS
{

public:
  void mostrar ();
  void capturar ();

private:
  char titulo[100];
  char clasificacion[100];
  char duracion[100];
  char horario[100];
  char costo[100];
  char formato[100];
};

Sugerencia 3

Ya que estamos programando en C++... usa C++. No tiene demasiado sentido que tu programa use char[] pudiendo usar std::string. Te quedará un código más seguro y legible.

class PELICULAS
{

public:
  void mostrar ();
  void capturar ();

private:
  std::string titulo;
  std::string clasificacion;
  std::string duracion;
  std::string horario;
  std::string costo;
  std::string formato;
};

Al usar std::string, la llamada a getline debería quedar así:

std::getline(std::cin, titulo);

Ya que std::cin no tiene ninguna sobrecarga de getline que admita un std::string.

Sugerencia 4

Tu bucle principal es un poco confuso. Si tu idea es que se salga al elegir la opción 3... ¿Por qué no haces justamente ese chequeo en el while?

int opcion; // Ya no usamos 'bucle' 

PELICULAS datos;
do
{
  // ...
}
while (opcion != 3);
eferion
  • 49,291
  • 5
  • 30
  • 72
1

Es un error pensar en el flujo de entrada como si fuera una secuencia, donde pides un valor, das enter, después pides otro, das enter, y asi sucesivamente, no funciona así.

En tu caso, tu tienes la intención de recibir de primera mano un número para el menú, un valor del 1 al 3, así que supongamos que doy 1, tu crees que escribes 1, das enter y el número 1 se guarda en la variable opcion, pero no escribiste solo 1, escribiste un 1 y un '\n' el cual este ultimo se quedo en el buffer de entrada, para hacerme entender, mira lo que pasa con este pequeño ejemplo:

#inclide <iostream>

int main()
{
    char nombre1[100];    
    int numero = 0;
    std::cout << "Escribe un numero cualquiera: ";
    std::cin >> numero;            
    std::cout << "Escriba su nombe: ";
    std::cin.getline(nombre1, 100, '\n');
    std::cout << "##################################################\n";
    std::cout << "Primer numero: " << numero << "\nNombre: " << nombre1 << std::endl;
    return 0;
}

Si lo ejecutas, te darás cuenta que el programa pide el número y no pide el nombre, de este pasa de largo, y cuando lo imprimes, se imprime el numero, pero el nombre queda vacio, esto paso porque quedo un '\n' en el buffer cuando ingresaste el número, asi que para evitar esto, lo más ideal y lo correcto, es que se limpie el buffer de entrada.

esta función la utilizo para limpiar el buffer de entrada

void limpiar_buffer()
{
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

cin.clear() lo que hace es limpiar los flags de error del stream interno, básicamente restableciendolos a no error, si no haces esto, la siguiente instrucción cin no se ejecutaría.

cin.ignore() básicamente lo que hace es que ignora todo los caracteres que haya en el buffer(limpiando el buffer), hasta que encuentre un '\n' o hasta que haya recorrido n caracteres, en este caso la longitud máxima que puede tener un stream de entrada.

Ahora con tu código, posee muchas inconsistencias,

Empecemos por la clase

Es una pésima practica de programación destapar los espacios de nombres(using namespace std), no lo hagas, si lo haces, este pierde el chiste para lo que realmente sirven. La clase quedaría así:

class PELICULAS 
{

public:    
    char titulo[100];
    char clasificacion[100];
    char duracion[100];
    char horario[100];
    char costo[100];
    char formato[100];

    void mostrar ();
    void capturar ();

};

notaras que quite da de la ultima línea de la definición de la clase, revisando tu código, le das el uso erróneo, Ademas, fácilmente tus variables de clase pueden ser string, no veo la necesidad de utilizar string-c o arreglos de char, te traerá más problemas utilizar estos que utilizar los string.

Las definiciones de las funciones de clase

No abuses tan indiscriminadamente de endl, que este lo que hace es escribir en la salida un salto de línea('\n') y limpiar el buffer de salida, ademas notaras que frente a cout, cin y endl hay un std::, eso es porque no destape todo el espacio de nombre.

void PELICULAS :: capturar()
{    
    std::cout<< "DATOS DE LAS PELICULAS"<< "\n\n";
    std::cout<< "Titulo de la pelicula: "<< "\n";
    limpiar_buffer();
    std::cin.getline(titulo,100);    
    std::cout<< "Clasificacion: "<< "\n";
    std::cin.getline(clasificacion,100);    
    std::cout<< "Duracion: "<< "\n";    
    std::cin.getline(duracion,100);
    std::cout<<"Horario: "<< "\n";    
    std::cin.getline(horario,100);    
    std::cout<<"Costo: "<< "\n";    
    std::cin.getline(costo,100);    
    std::cout<<"Formato: "<<std::endl;    
    std::cin.getline(formato,100);        
}

void PELICULAS::mostrar()
{
   std::cout<<"PELICULA EXISTENTE"<< "\n\n";
   std::cout<<titulo<< "\n";
   std::cout<< clasificacion<< "\n";
   std::cout<< duracion<< "\n";
   std::cout<<horario<< "\n";
   std::cout<<costo<< "\n";
   std::cout<<formato<<std::endl;
}

Te invito a que revises como se declaran las clases en C++, que al definir da ahí, lo que estas haciendo es instanciando la clase PELICULAS, en un objeto global, el cual nunca utilizas realmente en tu código.

Ahora el main

la variable bucle realmente no posee una utilidad real, ¿Por qué no utilizas solamente la variable opción para gestionar el bucle?, ademas, al definir la clase PELICULAS en cada case, estas limitando su ámbito solo a ese case, quiere decir que cuando el flujo del programa salga del case, la memoria de esa clase sera limpiada y se perderá toda la información que esta contenga, puedes notar donde yo definí a PELICULAS.

No utilices a system, estas llamando a todo el sistema solo para que te limpie la consola y acostumbrarse a utilizarlo puede ocasionar fugas de seguridad en un programa serio que realices en el futuro, te pongo un ejemplo, que tal que yo cambiara a cls por un programa malicioso, y que tu programa tenga permisos de administrador para correr, implícitamente le das permiso a ese programa que tu piensas que es cls. No te acostumbres a utilizarlo.

int main()
{

    int opcion = 0;    
    PELICULAS datos;
    do
    {                    
        std::cout<<"MENU PELICULAS"<< "\n\n";
        std::cout<<"1. Capturar"<< "\n";
        std::cout<<"2. Mostrar"<< "\n";
        std::cout<<"3. Salir"<< std::endl;        
        std::cout<<"ELIGE UNA OPCION: ";                
        std::cin >> opcion;
        switch(opcion)
        {
        case 1:
           {                
                datos.capturar();                
                break;
            }
        case 2:
            {                
                datos.mostrar();                
                break;  
            }
        case 3:
            {
                std::cout<< "\n" <<"G R A C I A S"<<std::endl;                
                break;
            }
        default:
            {
                std::cout<<"O P C I O N  N O  V A L I D A"<<std::endl;
            }
        }
    }
    while (opcion != 3);
    return 0;
}

Las cabeceras que utilizo son <iostream> y <limits>, no necesitas a <windows.h>, no la estas utilizando para nada en absoluto.

Mario
  • 653
  • 3
  • 19
  • ¿Por qué declaras el constructor vacio? ¿Y el destructor virtual? – eferion Feb 28 '19 at 07:58
  • podría llamarlo costumbre, no me acostumbro a definir el constructor con default o no definirlo. – Mario Feb 28 '19 at 08:02
  • Si lo declaras vacío puede dar lugar a dudas de si lo has dejado así a propósito o si se te ha olvidado rellenarlo, mientras que si pones `default` estás expresando la intencionalidad de dejarlo vacío. El no declararlo, desde mi punto de vista, estaría en un punto intermedio entre estos dos casos – eferion Feb 28 '19 at 08:38
  • @eferion tienes razón, con una pequña consulta puedo ver que si se define un constructor vacío la clase pasa a ser no trivial, y el compilador no asigna constructores de copia, movimiento, construcción y destrucción por defecto, en estos casos es mejor no declararla o declararla con `default`, ademas, siempre he dicho que se programa es para otra persona, así que el código debe ser lo más legible posible. – Mario Feb 28 '19 at 17:27
-2

¡Buenas noches!, no se si ya habras resuelto tu problema, ya qué es un problema muy sencillo... pero aún así se debe de entender bien el funcionamiento de en este caso el buffer y las funciónes para vaciarlo!, he aquí el código totalmente funcional:

#include<iostream>
#include<conio.h>//OJO ESTA LIBRERIA ES IMPORTANTE SIN ELLA NO PUEDES USAR FFLUSH()
using namespace std;

class PELICULAS {

public:
    char titulo[100];
    char clasificacion[100];
    char duracion[100];
    char horario[100];
    char costo[100];
    char formato[100];

void mostrar ();
void capturar ();

}da;

void PELICULAS :: capturar(){
    cout<< "DATOS DE LAS PELICULAS"<< endl<<endl;
    cout<< "Titulo de la pelicula: "<<endl;
    //Esta función limpiara el buffer para que no existan caracteres basura a la hora de guardar las variables
    fflush(stdin);
    cin.getline(da.titulo,100,'\n');
    cout<< "Clasificacion: "<<endl;
    cin.getline(da.clasificacion,100,'\n');
    cout<< "Duracion: "<<endl;
    cin.getline(da.duracion,100,'\n');
    cout<<"Horario: "<<endl;
    cin.getline(da.horario,100,'\n');
    cout<<"Costo: "<<endl;
    cin.getline(da.costo,100,'\n');
    cout<<"Formato: "<<endl;
    cin.getline(da.formato,100,'\n');
};

void PELICULAS::mostrar(){
   cout<<"PELICULA EXISTENTE"<<endl<<endl;
   cout<<da.titulo<< endl;
   cout<< da.clasificacion<< endl;
   cout<< da.duracion<<endl;
   cout<<da.horario<<endl;
   cout<<da.costo<<endl;
   cout<<da.formato<<endl;
};

int main(){

    int opcion, bucle;
    bucle =1;
    do
        {
        system("cls");
        cout<<"MENU PELICULAS"<<endl<<endl;
        cout<<"1. Capturar"<<endl;
        cout<<"2. Mostrar"<<endl;
        cout<<"3. Salir"<<endl;
        fflush(stdin);
        cout<<"ELIGE UNA OPCION: "; cin>>opcion;
        switch(opcion)
        {
        case 1:
           {
                system ("cls"); 
                PELICULAS datos;
                datos.capturar();
                system("pause");
                break;
            }
        case 2:
            {
                system ("cls");
                PELICULAS datos1;
                datos1.mostrar();
                system("pause");
                break;  
            }
        case 3:
            {
                bucle +=1;
                cout<<endl<<"G R A C I A S"<<endl;
                system("pause");
                break;
            }
        default:
            {
                cout<<"O P C I O N  N O  V A L I D A"<<endl;
                system("pause");
                break;
            }
    }
}
while (bucle == 1);
    return 0;
}

Habras notado en el código que hay tres cambios principales... estos son:

  • Utilice la función fflush con su parametro stdin (std input), es decir, con fflush(stdin); lo que se está haciendo primero es limpiar el buffer para que no hallan caracteres basura contenidos en la variable a la que le vas a insertar contenido.

  • Quite cin.ignore(), debido a que estaba repetido muchas vecez en prácticamente todos las lineas en las que "necesitabas limpiar el buffer", sin embargo, con que pongas fflush(stdin) al inicio antes de todos los valores que debes guardar con getLine() una sola vez por función o clase es suficiente (Como hice en el código).

  • Añadi un tercer parametro a cada cin.getLine(), el tercer parametro es un string (Entre comillas simples), y este parametro le permite saber a cin, cuando debe terminar una cadena de texto, en mi caso como coloque de tercer parametro un '\n', que traducido es un (Salto de linea), esto quiere decir que cada vez que cin encuentre un '\n', tomara la cadena por concluida, por lo tanto cada vez que des un enter, detectara la cadena completa.

Como nota importante, la función fflush(), solo se puede usar si haz importado primero la libreria conio.h.

Ah por otro lado, en tu código original se te olvido poner system("pause"); y luego break; en la sentencia default del switch, ya que si no pones system("pause"), si el usuario introduce un numero no valido, el cartel de "Opcion no valida", lo mostrará, pero lo mostrara y limpiara la pantalla tan rapido que no alcanzaras a ver ese mensaje en pantalla.

¡Qué tengas buenas noches!, cualquier cosa pregunta de nuevo.

Riven
  • 5,728
  • 2
  • 7
  • 27
  • 1
    No puedes decir que hay que comprender bien cómo funcionan los buffer y poner `fflush(stdin)`. Además mezclar la entrada salida propia de C++ con la heredada de C no es buena idea – eferion Feb 28 '19 at 06:26
  • 1
    Es decir, `fflush` está pensado para dispositivos **de salida** en el caso de los de entrada el comportamiento es indefinido, es decir, que a ti te funcione no implica que vaya a funcionar en todos los casos. Aunque ya puestos si estás en C++ limpia el buffer usando las clases propias de C++ – eferion Feb 28 '19 at 06:29
  • 1
    No. **La cabecera `` no es importante**, no en [tag:C++], de hecho tampoco es importante en [tag:C] porque [no pertenece al estándar de C](https://es.stackoverflow.com/a/122350/2742). La solución que propones no es correcta ni es adecuada. – PaperBirdMaster Feb 28 '19 at 06:55