Powrót do początku programu - do while, return main()?

0

Uszanowanie,
prowizoryczny program do wpłacania/wypłacania/sprawdzania stanu $. Mam pytanie odnośnie powracania do początku programu. Wyczytałem, że można to robić za pomocą pętli do-while, kombinuje cały czas, nie do końca mi wychodzi ale jeszcze próbuję. Gdzieś wyskoczyła opcja "goto", ale zaraz pod nią 1501239 komentarzy, żeby o tym zapomnieć to zapomniałem. W przypadku case 4 program ma za zadanie wylogować użytkownika i powrócić do początku. Czy można to zrobić za pomocą "return main()", czy jest to praktykowane? W tym przypadku jest to najprostsze rozwiązanie jakie udało mi się znaleźć.

#include <iostream>
#include <string>
#include <windows.h>
#include <stdlib.h>
#include <conio.h>

using namespace std;
string PIN;
char wybor;
float wplata, stan=5000, wyplata;
int main()

{
    cout << "Podaj PIN: ";
    cin >> PIN;
    system ("cls");
    cout << "LOADING..." <<endl;
    Sleep (1000);

    while(PIN != "0000")
    {
        cout<<"\a";
        cout << "Bledny PIN"<<endl;
        Sleep (1000);
        system("cls");
        cout << "Podaj PIN: ";
        cin >> PIN;
        cout << "LOADING..." <<endl;
        Sleep (1000);
        system("cls");
    }

    system ("cls");



    if (PIN=="0000")
    {

        for(;;)
        {
        cout << "=============="<<endl;
        cout << "Dostepne opcje"<<endl;
        cout << "=============="<<endl;
        cout << "1. Wplata gotowki"<<endl;
        cout << "2. Wyplata gotowki"<<endl;
        cout << "3. Stan konta" <<endl;
        cout << "4. Wyloguj" <<endl;
        cout << "5. Zamknij program"<<endl;
        cout <<"_____________________"<<endl;
        wybor = getch();
        cout<<endl;

        switch (wybor)
            {
            case '1':
               cout <<"Wplata gotowki"<<endl;
               cout <<"______________"<<endl;
               cout <<"Podaj wartosc: ";
               cin >> wplata;
               system ("cls");
               cout <<"Wplacono: "<< wplata <<endl;
               cout <<"Obecny stan konta: "<<stan+wplata<<endl<<endl<<endl;
               cout <<"Press any key to continue..."<<endl;
              break;

            case '2':
                cout <<"Wyplata gotowki"<<endl;
                cout <<"______________"<<endl;
                cout <<"Podaj wartosc: ";
                cin >> wyplata;

                if (wyplata>stan)
                    cout<< "Brak wystarczajacych srodkow!";
                    else
                {
                cout <<"Wyplacono: "<< wyplata <<endl;
                cout <<"Obecny stan konta: "<<stan-wyplata<<endl<<endl<<endl;
                cout <<"Press any key to continue..."<<endl;
                }
                break;

            case '3':
                cout << "Dostepne srodki: "<< stan <<endl<<endl<<endl;
                cout <<"Press any key to continue..."<<endl;
                break;

            case '4':
                system ("cls");
                return main();
                break;

            case '5':
                exit (0);
                break;

                default:
                cout<< "\a";
                cout<<"Blad! Dostepne opcje: 1-5"<<endl;
                cout<<"Wcisnij dowolny klawisz i wybierz ponownie!"<<endl;
            }
            getch();
            system("cls");
        }
        }


    return 0;
}
3

Czy można to zrobić za pomocą "return main()", czy jest to praktykowane?

Jest wręcz niewskazane. Nie wiem czy nie jest to UB.

  1. Wyrzuć całą logikę z instrukcji switch - case do osobnych funkcji. Łatwiej dostrzeżesz rozwiązanie, gdy kod będzie przejrzysty.
  2. Zmienne globalne to zło, pozbądź się ich, bo tutaj są niepotrzebne.
  3. Piszesz w C++, korzystaj z plików nagłówkowych przepisanych dla C++ <cstdlib>.
  4. W przypadku case 4 program ma za zadanie wylogować użytkownika i powrócić do początku.

Zamknij cały kod z funkcji głównej w pętli i instrukcję switch - case w drugiej poza którą program
będzie wychodził w przypadku wylogowania usera. Jakaś dodatkowa flaga, którą ustawisz w case 4:, a która spowoduje
przerwanie pętli.

0
YooSy napisał(a):

Czy można to zrobić za pomocą "return main()", czy jest to praktykowane?

Jest wręcz niewskazane. Nie wiem czy nie jest to UB.

Jak już napisano, nie jest to UB. Ale może powodować standardowe problemy z rekurencją (w końcu skończy siępamięć), jeśli nie masz włączonego optymalizowania rekurencji ogonowej (gcc -O3 chyba optymalizuje).

Z drugiej strony jest to aplikacja interaktywna, więc szanse, że się wywali z powodu zbyt głębokiej rekurencji są bardzo małe... Chyba, że ma działać przez lata bez resetu.

5

Jakbyś nie pisał wszystkiego w main to zrobiłbyś to już dawno sam.
Dziel kod na małe funkcje.
Sam problem jest prosty jak konstrukcja cepa: do { zawartoscMain; } while (jakiś warunek);

0

Rozwiązanie return main () znalazłem na jakimś forum, chciałem się dowiedzieć czy jest to ogólnie stosowane czy lepiej unikać. Dzięki za cenne wskazówki.

3

Co do main(), standard wypowiada się następująco:

The function main shall not be used within a program. The linkage ([basic.link]) of main is implementation-defined. A program that defines main as deleted or that declares main to be inline, static, or constexpr is ill-formed. The main function shall not be declared with a linkage-specification ([dcl.link]). A program that declares a variable main at global scope or that declares the name main with C language linkage (in any namespace) is ill-formed. The name main is not otherwise reserved. [ Example: Member functions, classes, and enumerations can be called main, as can entities in other namespaces. — end example ]

http://eel.is/c++draft/basic.start.main#3

Cały 6 pkt warto przeczytać by wyrobić sobie pogląd jaki jest status tej funkcji.

Nie nie jest to "ciekawy zabieg" i nie jest to "jakieś rozwiązanie".

0
Standard C++ §6.8.3.1:

The function main shall not be used within a program.

Czyli raczej kiepski pomysł z wywoływaniem main wewnątrz programu.

0

Próbuję zamknąć to w pętli do - while. Krótki przykład:

    char wybor=getch();
    string PIN="0000";
    getch();

    do
        {
        cout <<"Podaj PIN: ";
        cin >> PIN;
        system ("cls");
        cout << "LOADING..." <<endl;
        Sleep (1000);
        system("cls");
        }
    while (wybor == '4');

Sytuacja jest taka, że po odpaleniu Podaj PIN powinno pojawiać się tylko po wciśnięciu '4', a wyskakuje po wciśnięciu dowolnego klawisza.

1
int wybor=getch(); 
//...
    while (wybor == 52);

Zamiast sleep() użyj nowoczesnego rozwiązania std::this_thread::sleep_for() (<thread>). https://en.cppreference.com/w/cpp/thread/sleep_for?
system("cls") też nie jest najlepszym wyborem. Wystarczy wysunąć konsolę o odpowiednią ilość linii.

void clearConsoleScreen(){
   for(int i = 0; i < consoleHeight; ++i){
      std::cout << '\n';
   }
}
0
 int wybor=getch();

   do
   {
   cout << "OK" <<endl;
   }
    while (wybor == 4);

Wrzuciłem wybór jako int, ale problem ten sam. Po wciśnięciu dowolnego klawisza wyskakuje "OK". Nie mogę przypisać go tylko dla 4.

Rozumiem, że sleep() jest przestarzałym, niestosowanym rozwiązaniem? A czemu system("cls") nie jest najlepszym wyborem?

0

Ogólnie system() jest funkcją nieprzenośną (windowsową).

int myGetch() {
	int ch = getch();
	if (!ch) {
		return getch();
	}
	return ch;
}

int main() {
	std::cout << myGetch() << std::endl;
}

getch() zwraca dwa kody, aby łatwiej odróżniać znaki podstawowe od specjalnych.

std::cout << getch() << " : " << getch() << '\n';

Edit:
Nie zwróciłem na to uwagi:

do
{
   cout << "OK" <<endl; // to wykona się zawsze przed pierwszym sprawdzeniem warunku pętli.
}
    while (wybor == 4);

Tak jak w komentarzu. Wykonywanie programu w C++ jest od góry do dołu. Użyj do tego celu pętli while(){} lub for(;;).

0
#include <iostream>
#include <string>
#include <windows.h>
#include <cstdlib>
#include <conio.h>
#include <chrono>

using namespace std;
int main()
{
        string PIN="0000";
        char wybor;

        std::cout << "Podaj PIN: ";
        cin >> PIN;
        void ClearScreen();
        std::cout << string(50, '\n' );


        while (PIN != "0000")
        {
            std::cout << "Try again!"<< std::endl;
            clock_t a;
            a = clock () + 1 * CLOCKS_PER_SEC ;
            while (clock() < a){}
            std::cout << "Podaj PIN: ";
            cin >> PIN;
        }

        while (PIN == "0000")
        {
        clock_t a;
        a = clock () + 1 * CLOCKS_PER_SEC ;
        while (clock() < a){}

        {
            std::cout << "=============="<<std::endl;
            std::cout << "Dostepne opcje"<<std::endl;
            std::cout << "=============="<<std::endl;
            std::cout << "1. Wplata gotowki"<<std::endl;
            std::cout << "2. Wyplata gotowki"<<std::endl;
            std::cout << "3. Stan konta" <<std::endl;
            std::cout << "4. Wyloguj" <<std::endl;
            std::cout << "5. Zamknij program"<<std::endl;
            std::cout <<"_____________________"<<std::endl;
            wybor = getch();

        int wplata, wyplata, stan=5000;
        switch (wybor)
        {
            case '1':
               cout <<"Wplata gotowki"<<endl;
               cout <<"______________"<<endl;
               cout <<"Podaj wartosc: ";
               cin >> wplata;
               cout <<"Wplacono: "<< wplata <<endl;
               cout <<"Obecny stan konta: "<<stan+wplata<<endl<<endl<<endl;
               cout <<"Press any key to continue..."<<endl;
              break;

            case '2':
                cout <<"Wyplata gotowki"<<endl;
                cout <<"______________"<<endl;
                cout <<"Podaj wartosc: ";
                cin >> wyplata;

                if (wyplata>stan)
                    cout<< "Brak wystarczajacych srodkow!";
                    else
                {
                cout <<"Wyplacono: "<< wyplata <<endl;
                cout <<"Obecny stan konta: "<<stan-wyplata<<endl<<endl<<endl;
                cout <<"Press any key to continue..."<<endl;
                }
                break;

            case '3':
                cout << "Dostepne srodki: "<< stan <<endl<<endl<<endl;
                cout <<"Press any key to continue..."<<endl;
                break;

            case '5':
                exit (0);
                break;

                default:
                cout<< "\a";
                cout<<"Blad! Dostepne opcje: 1-5"<<endl;
                cout<<"Wcisnij dowolny klawisz i wybierz ponownie!"<<endl;
            }
            getch();
        }
    }
    return 0;
}

Udało mi się wysunąć konsolę o n linii (wrzuciłem to na razie tylko w przypadku gdzie user proszony jest o PIN) i niby ok, ale czy istnieje inne rozwiązanie na wyczyszczenie ekranu (aby działało na tej samej zasadzie co system ("cls"), gdzie cała konsola jest czyszczona niezależnie od OS?

Aby program czekał określony czas znalazłem jeszcze takie rozwiązanie:

string PIN="0000";

        clock_t a;
        a = clock () + 3 * CLOCKS_PER_SEC ;
        while (clock() < a){}

        std::cout << "Podaj PIN: ";
        cin >> PIN;

Czy zamiast sleep(); można je stosować/jest stosowane?

Cały czas nie mogę poradzić sobie z case 4, w którym program ma wylogować usera i powrócić do początku.
@YooSy mogę prosić o dokładniejsze wytłumaczenie jak ustawić flagę?

0
void Menu() {
	// wypisanie dostępnego menu
}

void Deposit(int& deposit, int& balance) {
	// dokonanie wpłaty
}

void Withdrawal(int& withdrawal, int& balance) {
	// wypłat, jeśli możliwa
}

void Balance(int balance) {
	// wypisz stan konta
}

std::string GetPIN(const std::string& correctPIN) {
	// pobranie PINu od użytkownika
}

char GetChoose() {
	Menu();
	// pobiera i zwraca wybór użytkownika
}

int main() {
	while (true) {
		const std::string correctPIN = "0000";
		int deposit, withdrawal, balance;
		char choose;
		bool flag = true;
		while (flag) {
			//Pobieranie danych od użytkownika
			std::string PIN = GetPIN(correctPIN);
			char choose = GetChoose();
			switch (choose) {
			case '1':
				Deposit(deposit, balance);
				break;
			case '2':
				Withdrawal(withdrawal, balance);
				break;
			case '3':
				Balance(balance);
				break;
			case '4':
				// zmiana statusu na wyjście z pętli
				flag = false;
				break;
			case '5':
				// zamknięcie programu
				std::quick_exit(0); // <cstdlib> od C++11
			default:
				// zły wybór
			} // end switch
		} // end while(flag)
	}// end while(true)
	return 0;
}
0

@YooSy skoro pokazujesz jak upraszczać, to ja bym się jeszcze przyczepił, że w funkcji main za dużo się dzieje. Już same komentarze wyglądające w ten sposób:

            } // end switch
        } // end while(flag)
    }// end while(true)

Pokazują, że kod trąci praktykami w stylu starego kodu C. Jeśli musisz komentować zamknięcia pętli to oznacza, ze za dużo masz upakowane w jedną metodę i zbyt wiele poziomów zagłębień. Tak w ogóle to może warto funkcję main uprościć do tego stopnia:

int main() {
    ATM MyATM;
    MyATM.run();
    return 0;
}
0
harlemshakiny napisał(a):

Aby program czekał określony czas znalazłem jeszcze takie rozwiązanie:

string PIN="0000";

        clock_t a;
        a = clock () + 3 * CLOCKS_PER_SEC ;
        while (clock() < a){}

        std::cout << "Podaj PIN: ";
        cin >> PIN;

Czy zamiast sleep(); można je stosować/jest stosowane?

Nie powinno się tego stosować - sleep, wraz z funkcjami podobnymi nie robi tego samego. To rozwiązanie marnuje procesor na bezczynność, podczas gdy sleep prosi procesor o nic nierobienie przez jakiś czas (tym samym nie marnuje zasobów).

1 użytkowników online, w tym zalogowanych: 0, gości: 1