Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Typ wyliczeniowy C++ - prośba o wyjaśnienie

Ostatnio zmodyfikowano 2016-09-11 13:35
Autor Wiadomość
jundymek
Temat założony przez niniejszego użytkownika
Typ wyliczeniowy C++ - prośba o wyjaśnienie
» 2016-08-28 22:32:46
Próbuję zrozumieć czym dokładnie jest typ wyliczeniowy w C++ i prosiłbym o pomoc w tym temacie. Z książki C++ Przewodnik dla początkujących zrozumiałem, że enum jest wykorzystywany do nazywania zmiennych, w zasadzie po to, żeby kod stał się czytelniejszy. Np. tworząc talie kart zamiast numerowania kart od 1 do 52 piszemy
enum karty { dwojka = 2, trojka = 3...dama = 10 };
 i mamy zmienne kart z przypisanymi do nich wartościami. Czy moje rozumowanie jest właściwe? Może mi ktoś wytłumaczyć w jaki sposób i kiedy należy korzystać z typu wyliczeniowego? Rozdział z cpp0x.pl opisujący to zagadnienie nie jest dla mnie dostatecznie jasny. Z góry dziękuję za pomoc.
P-151176
mateczek
» 2016-08-28 23:10:55
enum to inaczej int !!! tyle że zamiast cyfr używa się nazw. Enum to jedna zmienna a nie kilka
C/C++
#include <iostream>
using namespace std;
enum karty { dwojka = 2, trojka = 3, dama = 10 };

int main() {
    karty k;
    k = dwojka;
    cout << k << endl;
    k = trojka;
    cout << k << endl;
    k =( karty ) 10; //rzutowanie int na enum
    cout << k << endl;
   
}
P-151177
jundymek
Temat założony przez niniejszego użytkownika
» 2016-08-29 00:07:29
Niby jest to proste, ale jakoś nie mogę załapać zastosowania praktycznego. Może na praktycznym przykładzie dasz radę mi to wbić do głowy... Mam takie zadanie z książki: "Napisz program symulujący "Jednorękiego bandytę", który wyświetla użytkownikowi losowe wyniki. Niech każdy z bębnów maszyny przyjmuje co najmniej trzy możliwe wartości. Nie przejmuj się animacją tekstu "przelatującego" przed użytkownikiem; po prostu wylosuj wynik, wylosuj go i zaprezentuj wygraną (wygrywające kombinacje wybierz sam)."

Poniżej moja wersja takiego programu:
C/C++
#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

enum wartosci_bandyty {
    dwojka = 0,
    trojka = 1,
    dama = 2
};

void losowanie() {
    int wynik[ 3 ];
    for( int i = 0; i < 3; i++ ) {
        wynik[ i ] = rand() % 3;
        cout << wynik[ i ]; }
    if( wynik[ 0 ] == wynik[ 1 ] && wynik[ 1 ] == wynik[ 2 ] )
    { if( wynik[ 0 ] == 0 )
        { cout << "WYGRANA - trafiles 0"; }
        if( wynik[ 0 ] == 1 )
        { cout << "WYGRANA - trafiles 1"; }
        if( wynik[ 0 ] == 2 )
        { cout << "WYGRANA - trafiles 2"; } }
}

int main() {
    srand( time( NULL ) );
    char wybor;
    cout << "Gramy ? t/n: ";
    cin >> wybor;
    do
    { losowanie();
        cout << "\nGramy ? t/n: ";
        cin >> wybor; }
    while( wybor == 't' );
   
    return 0;
}

W jaki sposób mogę tu użyć typ wyliczeniowy? Na początku pisania utworzyłem sobie
C/C++
enum wartosci_bandyty {
    dwojka = 0,
    trojka = 1,
    dama = 2
};
ale nie miałem pomysłu jak to wykorzystać. W tego typu programie ma to w ogóle sens?

EDIT: Przerobiłem funkcję losującą w taki sposób:
C/C++
void losowanie() {
    int wynik[ 3 ];
    wartosci_bandyty wartosc;
    wartosc = dwojka;
    for( int i = 0; i < 3; i++ ) {
        wynik[ i ] = rand() % 3;
        cout << wynik[ i ]; }
    if( wynik[ 0 ] == wynik[ 1 ] && wynik[ 1 ] == wynik[ 2 ] )
    { if( wynik[ 0 ] == dwojka )
        { cout << "WYGRANA - trafiles 0"; }
        if( wynik[ 0 ] == trojka )
        { cout << "WYGRANA - trafiles 1"; }
        if( wynik[ 0 ] == dama )
        { cout << "WYGRANA - trafiles 2"; } }

Sam kod z ifami wydaje się czytelniejszy (dwojka, trojka, dama). Nie wiem tylko czemu muszę przypisywać na początku funkcji wartość zmiennej
wartosc = dwojka;
.
P-151183
michal11
» 2016-08-29 01:16:24
Enumy są przydane do tworzenia różnego rodzaju zmiennych w kodzie. Zastosowana jest wiele, na przykład zdefiniowanie kierunków ruch
C/C++
enum Direction { LEFT, RIGHT, UP, DOWN };

//...

obj.setNewDirection( Direction::DOWN );

albo określenie typu obrażeń
C/C++
enum DamageType { FIRE, COLD, ARCANE };

Często tez wykorzystuje się enumy z mapami jako indeksami
C/C++
enum Textures { CAR, INTERSECTION, LIGHTS };

std::map < Textures, sf::Texture > TexturesMap;

//...

sprite.setTexture( TexturesMap[ Textures::CAR ] );
P-151184
kmlkamilek
» 2016-09-09 12:37:11
Dziwi mnie, że nikt nie wspomniał o enumach z zasięgiem które są bezpieczniejsze w użyciu. Enumy pokazane przez kolegów wyżej, są enumami bez zasięgu. Co to oznacza? Weźmy na warsztat takiego enuma:

enum VALUES { VALUE1, VALUE2 };

To jest enum bez zasięgu, oznacza to, że nie musimy odwoływać się do konkrentego enuma, by dokopać się do jego wartości.

std::cout << static_cast < int >( VALUE1 ) << std::endl;

Ten kod wyświetli wartość pola VALUE1. Enumy, jeśli nie nadamy wartości ich polom, same nadadzą domyślne wartości od zera, więc w tym wypadku wartość pola VALUE1 będzie równa 0, a wartość pola VALUE2 będzie równa 1. Na pierwszy rzut oka, enumy bez zasięgu nie wydają się problematyczne, problem pojawi się w takim kodzie:

C/C++
#include <iostream>

char VALUE = 64;
enum VALUES { VALUE = 63 };

int main()
{
    std::cout << static_cast < int >( VALUE ) << std::endl;
   
    return 0;
}

Taki kod się nie skompiluje, bo inaczej wystąpiła by redeklaracja zmiennej VALUE. Odwołanie się do VALUE jest dwuznaczne, właśnie przez to, że enum VALUES jest bez zasięgu.


prog.cpp:4:24: error: 'VALUE' redeclared as different kind of symbol
 enum  VALUES { VALUE = 63 };
                        ^
prog.cpp:3:6: note: previous declaration 'char VALUE'
 char VALUE = 64;

To jak wyglądają enumy z zasięgiem? Wystarczy po enum wstawić słowo kluczowe
class
, lub
struct
.

enum class VALUES { VALUE = 63 };

Jeśli podmienimy naszego enuma bez zasięgu na enuma z zasięgiem, nasz kod się skompiluje i program wyświetli wartość 64.
Do pól takich enumów odwołujemy się jak do statycznych pól klasy. Chcąc wyświetlić pole VALUE z enuma VALUES, musimy się do tego enuma odwołać.

std::cout << static_cast < int >( VALUES::VALUE ) << std::endl;

To samo mogliśmy zrobić również z poprzednim enumem. Dla enumów bez zasięgu operator zasięgu również funkcjonuje jak należy.

Kolejną cechą enuma jest, to że jego wartościami są zmienne
constexpr
. Takie wartości działają tak samo jak zwykłe wartości const, lecz ich wartość jest znana w chwili kompilacji. Takie mogą być wykorzystane tam gdzie operacje przeprowadza się w momencie kompilacji, czyli np. w szablonach. Pole enuma może również przybrać wartość zwracaną przez funkcję, lecz zwracana wartość musi być znana w chwili kompilacji, więc co za tym idzie, musi być to wartość
constexpr

Kolejną bardzo ciekawą rzeczą jest to, że możemy zdefiniować jakiego typu będą pola enuma. Nie znaczy to, że każde osobne pole może być innego typu, a jest to typ określany dla każdego pola. Nie może być to każdy typ, musi być to typ całkowity, czyli np. char, short, long, long long i ich odpowiedniki nie uwzględniające wartości mniejszych od 0.

C/C++
enum class KEY
    : char
{ BACKSPACE = 10, TAB = 11, ESCAPE = 33 };
P-151583
karambaHZP
» 2016-09-09 12:49:49
Na pierwszy rzut oka, enumy bez zasięgu nie wydają się problematyczne, problem pojawi się w takim kodzie:
Dlatego nie powinno się mieszać konwencji nazewniczych.
P-151584
kmlkamilek
» 2016-09-10 12:15:02
Skoro standard C++11 pozwala na stworzenie enuma z zasięgiem, to czemu by nie skorzystać i nie martwić się takimi kolizjami w kodzie? ;)
P-151629
michal11
» 2016-09-10 14:32:15
Bo może się okazać, że nagle trzeba skompilować aplikacje na urządzenie na którym kompilator nie wspiera c++11 i wtedy po prostej zamianie enum class na enum wszystko się posypie, przy dobrej konwencji nazewniczej nie byłoby tego problemu.
Oczywiście kiedy można dobrze jest z tego korzystać ale nie należy bezwzględnie na tym polegać i trzeba pisać kod z głową.
P-151636
« 1 » 2
  Strona 1 z 2 Następna strona