Ir ao conteúdo
  • Cadastre-se

C++ Como retornar ao menu do meu programa em um 'switch'?


Visitante

Posts recomendados

Boa noite, eu fiz um programa que começa no menu principal, onde o usuário escolhe qual função ele deseja acessar.

Aí tem um comando 'switch' e após a execução do 'case', eu crio um comando 'IF' perguntando se o usuário quer voltar ao menu principal.

 

Aí que vem a dúvida, como eu posso fazer o programa retornar ao menu principal caso o usuário queira voltar? Eu costumo usar o comando 'goto' porque ele é bem prático, mas todo mundo diz que ele é uma gambiarra, então quais outras maneiras que eu posso fazer isso?

 

O código do programa tá aqui embaixo:

#include <iostream>
#include <locale.h>

using namespace std;

int main ()
{
    setlocale(LC_ALL,"");

    char opcao;
    cout << "Você está no menu." << endl << endl;
    cout << "R ---> Registro" << endl;
    cout << "C ---> Calculadora" << endl << endl;
    cout << "Digite uma das teclas acima para acessar a função desejada: ";
    cin >> opcao;
    system ("cls");

    switch (opcao)
    {
        case 'R':
            char retornar1;
            cout << "Você está na função 'Registro'." << endl << endl;
            cout << "Retornar ao menu? ---> S/N" << endl;
            cin >> retornar1;
            system ("cls");
            if (retornar1 == 'S') {
                //comando para retornar ao menu.
            } if (retornar1 == 'N') {
                cout << "Saindo do programa." << endl;
            }
            break;
        case 'C':
            char retornar2;
            cout << "Você está na função 'Calculadora'." << endl << endl;
            cout << "Retornar ao menu? ---> S/N" << endl;
            cin >> retornar2;
            system ("cls");
            if (retornar2 == 'S') {
                //comando para retornar ao menu.
            } if (retornar2 == 'N') {
                cout << "Saindo do programa." << endl << endl;
            }
            break;
    }

    return 0;
}

 

Valeu aí pra quem puder ajudar.

Link para o comentário
Compartilhar em outros sites

4 horas atrás, Ian Vianna disse:

Eu costumo usar o comando 'goto' porque ele é bem prático

Não! Definitivamente não use goto jamais.:o Existem outros mecanismos mais adequados para o olho humano.
 

#include <iostream>
#include <locale.h>

using namespace std;

char menu() {
    char opcao;
    cout << "Você está no menu." << endl << endl;
    cout << "R ---> Registro" << endl;
    cout << "C ---> Calculadora" << endl << endl;
    cout << "Digite uma das teclas acima para acessar a função desejada: ";
    cin >> opcao;
    cin.sync();
    system("cls");
    return opcao;
}

int main() {
    setlocale(LC_ALL, "");
    bool sair = false;

    while(sair != true) {
        switch(menu()) {
            case 'R':
                char retornar1;
                cout << "Você está na função 'Registro'." << endl << endl;
                cout << "Retornar ao menu? ---> S/N" << endl;
                cin >> retornar1;
                system("cls");
                cin.sync();

                if(retornar1 == 'S') {
                    //comando para retornar ao menu.
                    break;
                } if(retornar1 == 'N') {

                    cout << "Saindo do programa." << endl;
                    return 1;
                }


            case 'C':
                char retornar2;
                cout << "Você está na função 'Calculadora'." << endl << endl;
                cout << "Retornar ao menu? ---> S/N" << endl;
                cin >> retornar2;
                cin.sync();
                system("cls");

                if(retornar2 == 'S') {
                    //comando para retornar ao menu.
                    break;
                } if(retornar2 == 'N') {
                    cout << "Saindo do programa." << endl << endl;
                    return 2;
                }

            default:
                break;
        }
    }

    return 0;
}

Como você pode ver não há mistério.com o break você quebra o switch nesse ponto "saindo fora" dele, e com o return você quebra a execução do programa nesse ponto aparte de retornar um valor inteiro qualquer que lhe seja útil para fins de depurado ou informação.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

6 horas atrás, Ian Vianna disse:

Aí tem um comando 'switch' e após a execução do 'case', eu crio um comando 'IF' perguntando se o usuário quer voltar ao menu principal.

 

Aí que vem a dúvida, como eu posso fazer o programa retornar ao menu principal caso o usuário queira voltar? Eu costumo usar o comando 'goto' porque ele é bem prático, mas todo mundo diz que ele é uma gambiarra, então quais outras maneiras que eu posso fazer isso?

 

Isso vem desse artigo, de 1968 ;)  

Uma opinião mais recente em CPPCON 2020 cita coisas como essa, mitos da programação. Pode ver os dois e tirar alguma conclusão. Mas veja o exemplo que está bem aqui no seu post...

 

Não precisa ser muito observador para entender por exemplo que 

  • goto implica em desviar a execução do programa para o comando seguinte a um label
  • switch implica em uma série de labels, ao menos um. Claro, pode não ter nenhum mas aí também não faz nada
  • switch tendo labels desvia a execução para o comando depois do case correspondente
  • um break desvia a execução para o comando seguinte ao switch
  • um switch então equivale a dois goto "estilizados"

Exemplo em C++

 

#include <iostream>
int main (void)
{
    int valor = 0;
    
    pontoA: switch(valor)
    {
        case 0:
            valor = 1;
            break;
        case 10:
            valor = 355;
            break;
        default:
            valor = 0;
            break;
    };

    pontoB:  if ( valor == 1 ) goto pontoA;

    std::cout << "saindo com valor = " << 
        valor << std::endl;
    return 0;
}

 

E o código gerado no gcc 9.3 por exemplo onde se vê o que vai ser executado de verdade, com as instruções que o compilador gera na saída:

 

.LC0:
        .string "saindo com valor = "
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 0
.L2:
        cmp     DWORD PTR [rbp-4], 0
        je      .L3
        cmp     DWORD PTR [rbp-4], 10
        je      .L4
        jmp     .L9
.L3:
        mov     DWORD PTR [rbp-4], 1
        jmp     .L6
.L4:
        mov     DWORD PTR [rbp-4], 355
        jmp     .L6
.L9:
        mov     DWORD PTR [rbp-4], 0
        nop
.L6:
        cmp     DWORD PTR [rbp-4], 1
        jne     .L7
        jmp     .L2
.L7:
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     rdx, rax
        mov     eax, DWORD PTR [rbp-4]
        mov     esi, eax
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
        mov     eax, 0
        leave
        ret
__static_initialization_and_destruction_0(int, int):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        cmp     DWORD PTR [rbp-4], 1
        jne     .L12
        cmp     DWORD PTR [rbp-8], 65535
        jne     .L12
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        call    __cxa_atexit
.L12:
        nop
        leave
        ret
_GLOBAL__sub_I_main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 65535
        mov     edi, 1
        call    __static_initialization_and_destruction_0(int, int)
        pop     rbp
        ret

 

 

E você vê que

  • o switch() está em .L2 --- label 2 :) --- e lá compara valor com 0 e vai para .L3, o label case 0: je é um goto: Jump on Equal. 
  • Se não for 0 compara com 10 e se for igual vai para .L4, outro goto 
  • Se não for 10 então é o label default. E faz valor = 0 e usa JMP o goto incondicional, jump, e vai para o ponto B
.L2:
        cmp     DWORD PTR [rbp-4], 0
        je      .L3
        cmp     DWORD PTR [rbp-4], 10
        je      .L4
        jmp     .L9
  • calma, já vou terminar
     
  • veja o código em pontoB
     
        pontoB:  if ( valor == 1 ) goto pontoA;


    E o que vira
     

  • .L6:
            cmp     DWORD PTR [rbp-4], 1
            jne     .L7
            jmp     .L2
    .L7:
            mov     esi, OFFSET FLAT:.LC0
            mov     edi, OFFSET FLAT:_ZSt4cout
    ...

     

Pois é: dois goto: um que está no programa, o goto pontoA, e outro que vai ger gerado para o programa seguir pro cout afinal...

 

Conclusão

 

Desconfie. Muito disso é frescura. Assista à conferência CPPCON de que falei por exemplo. Esse artigo sobre os goto foi super impulsionado porque o editor da CACM na época (1968), onde saiu o artigo, o prof Wirth, criador do Pascal, reforçava totalmente o uso da tal programação estruturada, o hit da época, em que a gente programava em FORTRAN ou COBOL ou Assembler, e alguns em Algol, que o prof Wirth "baseou" a linguagem Pascal que ele "criou". Política também conta. Ele queria divulgar os conceitos que ele achava que eram essenciais para a vida humana e que estão no prefácio do Pascal User Manual And Report, a bíblia do Pascal, escrita por... ele mesmo.

 

Porque não se usa goto afinal?

 

Porque há opções melhores. O switch por exemplo é bonitinho.

 

Porque se usa goto afinal?

 

Porque em alguns casos ainda é perfeitamente justificável e melhor. Exemplos? Assista a conferência. Ou apenas continue programando e deve encontrar um ou outro. E entenda que um CALL por exemplo é um par de GOTO, um switch como eu mostrei é também. 

 

E sem JMP ou JE não existe programa afinal :) 

 

image.png

  • Amei 1
Link para o comentário
Compartilhar em outros sites

@vangodp Evitar goto ao máximo é bom, mas não à todo custo.

Nesse código do autor realmente não é necessário, mas acredito que aumentar a complexidade em nome da regra em certos casos possa ser inconveniente.

O uso parcimonioso desse comando por quem sabe usar de fato não causa problemas nem mesmo de legibilidade.

Link para o comentário
Compartilhar em outros sites

34 minutos atrás, Lucca Rodrigues disse:

@vangodp Evitar goto ao máximo é bom, mas não à todo custo.

Nesse código do autor realmente não é necessário, mas acredito que aumentar a complexidade em nome da regra em certos casos possa ser inconveniente.

O uso parcimonioso desse comando por quem sabe usar de fato não causa problemas nem mesmo de legibilidade.

 

Sim!

 

A vida não começa com um programa. Começa com um problema a ser resolvido, um projeto, um orçamento, uma aposta, um exercício, um prazo...

 

A partir daí, se isso vai gerar um programa, se cria uma abstração, uma representação para o problema. Aí vem uma linguagem, umas pessoas, uns comandos. E em algum momento um resultado.

 

E pode ser que o particular algoritmo seja bem representado em C ou C++ ou C# ou FORTRAN ou COBOL ou java ou Python ou Go ou Assembler... Exceto Python toda linguagem tem esse comando. E não faltam linguagens.

 

É possível fazer coisas sérias em Python, já que a linguagem tem bibliotecas para TUDO, escritas em C ou C++.

 

Exemplo

 

Um tipo de problema sério que é muito adequado de implementar com goto é uma máquina de estados, conhecida como FSM --- finite state machine --- na literatura. Isso é importante pra c@r@lh0 porque? Porque representa qualquer coisa que tenha, quem diria, um número finito de estados. Como um compilador, um parser, um protocolo de comunicações, um processo administrativo...

 

E implementar uma FSM dentro de um switch() ou if() não acrescenta absolutamente nada positivo: só fica artificial e mais difícil de ler. Na dúvida, escreva uma.

3 horas atrás, vangodp disse:

Lhe sugiro que faça um programinha cheio de gotos para ter uma ideia do "problema"

 

Trata-se de uma inversão de papéis: primeiro vem "a ideia do problema", depois a abstração, o modelo conceitual, depois a implementação. E em alguns casos a implementação é mais legível e melhor escrita usando esses condenados e criminosos e harmful goto, em que pese a opinião do prof. Wirth, do prof Dijkstra e outros. A realidade prevalece. Na dúvida, escreva e teste, por exemplo, um  autômato.

Link para o comentário
Compartilhar em outros sites

Não sei por que tanto papo... goto é só um recurso mais da linguagem, ta ai por "retrocompatibilidade", é uma coisa que não vão tirar nunca do cpp/c. Realmente os que são "partidários" do goto aqui no post usam isso a diário? XDDD

Se querem ver goto vão dar uma olhada no código do linux XDDD

E olho antes que venham por 200 linhas aqui embaixo. Não to criticando linux, como falei é só um recurso mais da linguagem, ponham se quiser. Igual que o switch também n é necessário... ja que pode ser trocado facilmente por ifs e outros.

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...