[C++]Kolko krzyzyk z uzyciem obiektow w tym Singletona

Draqun

Użytkownik
Dołączył
Sierpień 27, 2007
Posty
67
Postanowiłem dalej zgłębiać tajemnice programowania obiektowego. Brnąc dalej z kursem "Od zera do gier kodera" ukończyłem dział związany z Obiektami i chciałem zrobić kolko krzyzyk obiektowo. Kod zaczyna się kompilować ale nagle wywala mi masę błędów. Oto kod.
main.cpp
Kod:
#include<iostream>
#include"lib.h"
using namespace std;

int main()
{
    string imie1;
    Gracz *gracz1, *gracz2;
    Plansza *Obiekt;
    cout << "Podaj nazwy graczy" << endl;
    cin>>imie1;
    gracz1=new Gracz(imie1);
    cin>>imie1;
    gracz2=new Gracz(imie1);
    Obiekt->Creat(gracz1, gracz2);
    Obiekt->graj();
    return 0;
}

lib.h
Kod:
#include<iostream>
using namespace std;

enum FINAL{FIN_WIN, FIN_LOSE, FIN_TIE, FIN_EMPTY}; //przygotowuje sobie typ majacy 4 wartosci dostosowane do wygranej, przegranej, remisu, i pustego, innej wartosci zmienna typu
//FINAL nie przyjmie

//class Akcja;//klasa odpowiedzialna za 'akcje' w grze tutaj deklaruje bo potrzebna bedzie deklaracja obiektu tego typu w klasie plansza

class Akcja
{
    private:
    char pole[3][3];
    FINAL koniec;//zmienna pilnujaca czy jest koniec gry
    bool komputer;//zmienna sprawdzajaca kto ma ruch
    public:
        void wypelnij();
        void rysuj();
        int start();
        int sprawdz(int, char);
        void wynik();
};

class Gracz//klasa przchowujaca dane o graczach
{
    private:
        const string imie;
    public:
        Gracz(string nazwa)
            :imie(nazwa)
        {}
        ~Gracz(){};

};

class Plansza//Singleton poniewaz nie potrzebujemy wiecej plansz do gry w kolko krzyzyk
{
    private:
            const Gracz *imie1;//imiona graczy
            const Gracz *imie2;
            Plansza(Gracz *numer1, Gracz *numer2)//konskrtuktor singletona
            :imie1(numer1), imie2(numer2)//nadanie imion stałym
            {cout<<imie1 << " " <<imie2 <<endl; Istnieje=this;};
    public://zmienna potrzebna do sprawdzania czy juz mamy naszego Singletona
            static Plansza *Istnieje;
            static Plansza *Creat(Gracz *nazwa1, Gracz *nazwa2)//w zaleznosci od wartosci Istnieje tworzymy singletona :P
            {
                if(Istnieje==NULL)
                {
                    Plansza(nazwa1, nazwa2);//wywolanie konstruktora
                }

                return  Istnieje;
            }
            void wypisz(int a)
            {
                if(a==1)
                {
                    cout<<imie1;
                }
                else
                    cout<<imie2;
            }
            Akcja *Gramy;//obiekt typu akcja
            void graj()
            {
                Gramy->start();
            }
            ~Plansza(){Istnieje=NULL;}//destruktor

};

akction.cpp
Kod:
#include<iostream>
#include<conio.h>
#include<ctime>
#include"lib.h"
Plansza *Istnieje=NULL;

using namespace std;
void Akcja::wypelnij()
{
    for(int a=0;a<3;a++)
    {
        int c;
        if(a==0){c=7;}
        else{if(a==1){c=5;}else c=1;}
        for(int b=0;b<3;b++)
        {
            pole[a][b]=c;
            c++;
        }
    }
    koniec=FIN_EMPTY;
}
void Akcja::rysuj()
{
    cout<<"KOLKO KRZYZY" <<endl;
    Plansza *Pomoc;
    cout<<"Ruch wykonuje "; if(komputer==false){Pomoc->wypisz(1); cout<<endl;} else{Pomoc->wypisz(0); cout<<endl;};
    for(int a=0; a<3; a++)
    {
        for(int b=0; b<3; b++)
            cout<<pole[a][b];
        cout<<endl;
    }
    cout<<"Podaj miejsce ruchu" <<endl;
}

int Akcja::start()//funkcja odpowiedzialna za rozpoczecie gry,
{
    srand(time(NULL));
    int b=rand();
    if(b%2==0) komputer=false;
    else{komputer=true;}
    int a=0;

    do{
        wypelnij();
        rysuj();
        if(komputer==false)
        {
            a=getch();
            switch(a)
            {
                case 49:{sprawdz(a,'X');break;}
                case 50:{sprawdz(a,'X');break;}
                case 51:{sprawdz(a,'X');break;}
                case 52:{sprawdz(a,'X');break;}
                case 53:{sprawdz(a,'X');break;}
                case 54:{sprawdz(a,'X');break;}
                case 55:{sprawdz(a,'X');break;}
                case 56:{sprawdz(a,'X');break;}
                case 57:{sprawdz(a,'X');break;}
                case 107:{break;}
                default: {cout<<"Zly klawisz" <<endl;}
            }
            komputer=true;
        }
        else
        {
            do{
            srand(time(NULL));
            int a=rand()%9+1;
            b=sprawdz(a, '0');
            }while(b!=0);
            komputer=false;
        }
    }while(a!=107 || koniec!=FIN_LOSE || koniec!=FIN_TIE || koniec!=FIN_WIN);
    if(koniec==FIN_WIN)
    {
        cout<<"Wygrales!" <<endl;
    }
    else
    {
        if(koniec==FIN_LOSE)
        {
            cout<<"Przegrales." <<endl;
        }
        else
        {
            cout<<"Remis." <<endl;
        }
    }
    return 0;
}
int Akcja::sprawdz(int liczba, char ruch)//funkcja  sprawdza czy na danym miejscu w tablicy jest miejsce wolne aby wstawic nasz znak - jako parametr przyjmuje miejsce wstawienia znaku i znak
{
            if(liczba==7)//sprawdzamy dla 7 bo 7 jest 1 na tablicy. Patrz jak zbudowana tablica w bibliotece
            {
                if(pole[0][0]!='X' && pole[0][0]!='O')//sprawdzamy czy nie ma tam juz kolka ani krzyzyka
                pole[0][0]=ruch;//jesli bulo wolne wstawiamy nasz znak
                else//jesli jest olko albo krzyzyk zwracamy 1 do funkcji akcja
                return 1;
            }
            else{
                if(liczba==8)//analogicznie do tego co wyzej i tak samo jest ponizej
                {
                    if(pole[0][1]!='X' && pole[0][1]!='O')
                    pole[0][1]=ruch;
                    else
                    return 1;
                }
                else{
                    if(liczba==9)
                    {
                        if(pole[0][2]!='X' && pole[0][2]!='O')
                        pole[0][2]=ruch;
                        else
                        return 1;
                    }
                    else
                        {
                        if(liczba==4)
                        {
                            if(pole[1][0]!='X' && pole[1][0]!='O')
                            pole[1][0]=ruch;
                            else
                            return 1;
                        }
                        else
                            {
                            if(liczba==5)
                            {
                                if(pole[1][1]!='X' && pole[1][1]!='O')
                                pole[1][1]=ruch;
                                else
                                return 1;
                            }
                            else
                                {
                                if(liczba==6)
                                {
                                    if(pole[1][2]!='X' && pole[1][2]!='O')
                                    pole[1][2]=ruch;
                                    else
                                    return 1;
                                }
                                else
                                    {
                                    if(liczba==1)
                                    {
                                        if(pole[2][0]!='X' && pole[2][0]!='O')
                                        pole[2][0]=ruch;
                                        else
                                        return 1;
                                    }
                                    else
                                        {
                                        if(liczba==2)
                                        {
                                            if(pole[2][1]!='X' && pole[2][1]!='O')
                                            pole[2][1]=ruch;
                                            else
                                            return 1;
                                        }
                                        else
                                            {
                                            if(liczba==3)
                                            {
                                                if(pole[2][2]!='X' && pole[2][2]!='O')
                                                pole[2][2]=ruch;
                                                else
                                                return 1;
                                            }
                                            else//jesli podamy cos z innego zakresu niz 1-9 to zwroci 1 a wiec ruch sie powtorzy
                                                {
                                                    return 1;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
        return 0;
}

void Akcja::wynik()//sprawdza wynik
{    //tutaj sprawdzamy czy My wygralismy
    if((pole[0][0]=='X' && pole[1][0]=='X' && pole[2][0]=='X') || (pole[0][1]=='X' && pole[1][1]=='X' && pole[2][1]=='X') || (pole[0][2]=='X' && pole[1][2]=='X' && pole[2][2]=='X') || (pole[0][0]=='X' && pole[0][1]=='X' && pole[0][2]=='X') || (pole[1][0]=='X' && pole[1][1]=='X' && pole[1][2]=='X') || (pole[2][0]=='X' && pole[2][1]=='X' && pole[2][2]=='X') || (pole[0][0]=='X' && pole[1][1]=='X' && pole[2][2]=='X') || (pole[2][0]=='X' && pole[1][1]=='X' && pole[0][2]=='X'))
    {
        koniec=FIN_WIN;
    }
    else
    {   //jesli my nie wygralismy sprawdzamy czy komputer wygral
        if((pole[0][0]=='O' && pole[1][0]=='O' && pole[2][0]=='O') || (pole[0][1]=='O' && pole[1][1]=='O' && pole[2][1]=='O') || (pole[0][2]=='O' && pole[1][2]=='O' && pole[2][2]=='O') || (pole[0][0]=='O' && pole[0][1]=='O' && pole[0][2]=='O') || (pole[1][0]=='O' && pole[1][1]=='O' && pole[1][2]=='O') || (pole[2][0]=='O' && pole[2][1]=='O' && pole[2][2]=='O') || (pole[0][0]=='O' && pole[1][1]=='O' && pole[2][2]=='O') || (pole[2][0]=='O' && pole[1][1]=='O' && pole[0][2]=='O'))
        {
            koniec=FIN_LOSE;
        }
        else
        {   //jesli ani my ani komputer nie wygral to sprawdzamy, moze jest remis
            if((pole[0][0]=='O' || pole[0][0]=='X') && (pole[0][1]=='O' || pole[0][1]=='X') && (pole[0][2]=='O' || pole[0][2]=='X') && (pole[1][0]=='O' || pole[1][0]=='X') && (pole[1][1]=='O' || pole[1][1]=='X') && (pole[1][2]=='O' || pole[1][2]=='X') && (pole[2][0]=='O' || pole[2][0]=='X') && (pole[2][1]=='O' || pole[2][1]=='X') && (pole[2][2]=='O' || pole[2][2]=='X'))
            {
                koniec=FIN_TIE;
            }
        }
    }
}
Bugi
Kod:
-------------- Build: Debug in 0iXObiektowo ---------------

Compiling: main.cpp
Compiling: action.cpp
Linking console executable: bin\Debug\0iXObiektowo.exe
obj\Debug\main.o(.text$_ZN7Plansza5CreatEP5GraczS1_[Plansza::Creat(Gracz*, Gracz*)]+0x8): In function `ZN5GraczC1ESs':
C:/Users/draqun/Documents/CPP/0iXObiektowo//lib.h: undefined reference to `Plansza::Istnieje'
obj\Debug\main.o(.text$_ZN7Plansza5CreatEP5GraczS1_[Plansza::Creat(Gracz*, Gracz*)]+0x34):C:/Users/draqun/Documents/CPP/0iXObiektowo//lib.h: undefined reference to `Plansza::Istnieje'
obj\Debug\main.o(.text$_ZN7PlanszaD1Ev[Plansza::~Plansza()]+0x5):C:/Users/draqun/Documents/CPP/0iXObiektowo//lib.h: undefined reference to `Plansza::Istnieje'
obj\Debug\main.o(.text$_ZN7PlanszaC1EP5GraczS1_[Plansza::Plansza(Gracz*, Gracz*)]+0x64):C:/Users/draqun/Documents/CPP/0iXObiektowo//lib.h: undefined reference to `Plansza::Istnieje'
 
Ostatnia edycja:

discovery44

Były Moderator
Dołączył
Sierpień 14, 2007
Posty
763
Gdybyś poczytał log z kompilacji zobaczyłbyś powiązanie pomiędzy błędami - każdy dotyczył obiektów statycznych, zatem powinieneś już w google szukać "c++ składniki statyczne w klasie". Jednak skoro już przeczytałem ten log przerywając sobie konsumpcję kanapki z majonezem:
Jerzy Grębosz napisał:
Deklaracja składnika statycznego w ciele klasy nie jest jego definicją. Definicję musimy umieścić gdzieś tak, by miała zakres pliku. Czyli tak, jakbyśmy umieszczali definicję zmiennej globalnej.
W Twoim wypadku będzie to coś na wzór:
akction.cpp
Kod:
#include<iostream>
#include<conio.h>[FONT=monospace]
[/FONT]#include<ctime>[FONT=monospace]
[/FONT]#include"lib.h"
using namespace std;
int Plansza::Istnieje = 0;
...
 

Draqun

Użytkownik
Dołączył
Sierpień 27, 2007
Posty
67
@discovery44
Niestety tamto rozwiązanie nie pomogło. Wynikało to z paru czynników m.in. źle napisanego kodu przeze mnie.

Niestety problemy nie zaginęły. O ile powracając do konkretnych działów kursu poprawiłem i zoptymalizowałem lekko kod tak dalej dostaje błędy dotyczące static Plansza *Istnieje.

Po mimo udanej próby przypisania do tej zmiennej statycznej NULLa - o co zresztą wcześniej mi się nie udawało program nie daje się skompilować.
 

discovery44

Były Moderator
Dołączył
Sierpień 14, 2007
Posty
763
Ludzie... jak Ci wywala błędy przy kompilacji to je pokaż tutaj, nie jestem jasnowidzem, sorry.
 

Draqun

Użytkownik
Dołączył
Sierpień 27, 2007
Posty
67
Błedy są w 1 poście, żeby nie robić burdelu podmieniam niekatualny kod/bugi na aktualne. Zapomniałem o tym wspomnieć (przy czym zawsze tak robię (na tym forum mnie tak nauczono ;))). Ale jak chcesz podam i tu
Kod:
||=== 0iXObiektowo, Debug ===|
)]+0x8)||In function `ZN5GraczC1ESs':|
C:\Users\draqun\Documents\CPP\0iXObiektowo\lib.h||undefined reference to `Plansza::Istnieje'|
)]+0x34):C:\Users\draqun\Documents\CPP\0iXObiektowo\lib.h||undefined reference to `Plansza::Istnieje'|
obj\Debug\main.o(.text$_ZN7PlanszaD1Ev[Plansza::~Plansza()]+0x5):C:\Users\draqun\Documents\CPP\0iXObiektowo\lib.h||undefined reference to `Plansza::Istnieje'|
)]+0x64):C:\Users\draqun\Documents\CPP\0iXObiektowo\lib.h||undefined reference to `Plansza::Istnieje'|
||=== Build finished: 4 errors, 0 warnings ===|
 

Draqun

Użytkownik
Dołączył
Sierpień 27, 2007
Posty
67
Za pomocą #include w Windowsie. Nie chciało mi się pisać makefile w Linuchu :)
 

loganek

Były Moderator
Dołączył
Listopad 11, 2006
Posty
563
... widze że ciężko będzie.
Nie widzę nigdzie czegoś takiego: #include "akction.cpp", więc pytam, w jaki sposób dołączasz ten plik. Czy masz go dołączony do projektu? jeśli tak, to w jaki sposób, z jakiego IDE korzystasz
 

Draqun

Użytkownik
Dołączył
Sierpień 27, 2007
Posty
67
Z tego co wiem z kursu (mówię o kursie "Od zera do gier kodera") jeśli metody mam zapisane w innym pliku niż deklarację klasy, to tylko do tego pliku dołączam plik, w którym mam deklarację klasy tj. klasę mam zadeklarowaną w pliku lib.h a jej metody są w pliku action.cpp więc idąc tokiem myślenia tylko do action.cpp dołączam lib.h. Do tej pory wystarczyło to do tego aby kompilator powiązał sobie klasę i jej metody i aby te proste programiki działały.

Jeśli chodzi o IDE to korzystam z CodeBlocks.

EDYTKA:

A i tak wszystkie te pliki są dołączone do projektu.
 
Ostatnia edycja:

loganek

Były Moderator
Dołączył
Listopad 11, 2006
Posty
563
no to coś chyba źle to łączysz. w każdym razie, spróbuj wywalić wszystko z projektu(zostaw tylko main), i w main dopisz:
#include "akction.cpp"
a w akction.cpp dopisz:
#include"lib.h"
I zobacz czy zadziała
 

discovery44

Były Moderator
Dołączył
Sierpień 14, 2007
Posty
763
To nic nie zmieni bo nie widać błędów o nieznajomości obiektów składowych i nazw klas z pliku lib.h, na dodatek przy tworzeniu projektu w C::B i dołączaniu do niego plików C::B samo dopisuje pliki do linkowania więc z tym problemów nie ma. Gorzej gdyby np. kompilował sam plik main.cpp nie linkując pozostałych ale w tej sytuacji sprawa jest prosta. To wina jednego ze składników klasy Plansza, jakiś czas nie pisałem w obiektowym C++ i nie jestem w stanie z pamięci tego napisać ale wydaje mi się, że dziwnie wygląda umiejscowienie wskaźnika Istnieje. Za jakiś czas odpiszę jeszcze a na razie sam poszukaj odpowiedzi.
 

loganek

Były Moderator
Dołączył
Listopad 11, 2006
Posty
563
hmm, kiedy sprawdzałem takie dołączanie plików jakie zaproponowałem w poprzednim poście, kompilacja przebiegła bez problemów
 

discovery44

Były Moderator
Dołączył
Sierpień 14, 2007
Posty
763
Ja nie twierdzę, że ten sposób jest zły tylko, że to już jest robota wykonana przez IDE więc nie ma po co 2 razy tego robić.
 

loganek

Były Moderator
Dołączył
Listopad 11, 2006
Posty
563
mówiłem też żeby wywalić wszystko z projektu - więc raczej nie będzie to robota 2 razy wykonana
 
Do góry Bottom