• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Koneksje pomiędzy dwoma obiektami różnych (nieznanych) typów. Polimorfizm?

Object Storage Arubacloud
0 głosów
276 wizyt
pytanie zadane 14 grudnia 2017 w C i C++ przez MikeMG Początkujący (330 p.)
edycja 15 grudnia 2017 przez MikeMG

[ROZWIĄZANY]
Witam. Mam problem. Postanowiłem napisać program, który podsumowywałby całą moją wiedzę z obiektowego C++. Program miał za zadanie być łatwo modyfikowalną areną, gdzie dwa obiekty różnych typów (np. Wilk i Niedźwiedź) walczyłyby ze sobą. Obie klasy dziedziczą z klasy abstrakcyjnej "Postać". Z zapisem nie mam problemu, jednak schody zaczynają się wtedy, gdy jeden obiekt ma zadać obrażenia drugiemu. Każda klasa dysponuje kilkoma argumentami:

hp - zdrowie,
ap - siła ataku,
fp - punkty ucieczki (wpływają na prawdopodobieństwa ucieczki z trudnej walki)

Jak mam zapisać sytuację, gdy obiekt klasy Niedźwiedź zadaje obrażenia obiektowi klasy wilk? Chciałbym, aby atrybuty obu klas pozostały prywatne, a żeby "wymiana ciosów" odbywała się poprzez publiczne metody tychże klas. Z góry dziękuję za pomoc, wklejam kod:

#include <iostream>
#include <math.h>
#include <windows.h>
#include <cstdlib>
#include <time.h>
#include "rng.h"
using namespace std;

class Postac
{
public:
    friend int rng(int,int); // Moja własna funkcja do losowania liczb. Nieistotne.
    virtual void atakuj()=0;
    virtual void uderzony()=0;
    virtual void uciekaj()=0;
};

class Niedzwiedz :public Postac
{
    string nazwa;
    int hp; // 1 - 100: Zdrowie
    int ap; // 1 - 100: Sila
    int fp; // 1 - 10: Szansa na ucieczke
public:
    friend int rng(int,int);
    Niedzwiedz(string Niedzwiedz, int health=100, int attack=25, int flee=4)
    {
        hp=health;
        ap=attack;
        fp=flee;
    }
    virtual void atakuj()
    {
        // ???
    }
    virtual void uderzony()
    {
        // ???
    }
    virtual void uciekaj()
    {
        if(rng(1,10)<=rng(fp-1,3))
        {
            system("cls");
            cout<<"Obiekt "<<nazwa<<" uciekl pomyslnie!"<<endl;
            Sleep(2000);
            exit(0);
        }
        else
        {
            system("cls");
            cout<<"Ucieczka nieudana! (-"<<(10-fp)*5<<" HP)!"<<endl;
            hp-=(10-fp)*5;
            Sleep(2000);
        }
    }

};

int main()
{
    srand(time(NULL));
    
    
   // jakiś kod... co dalej?


    return 0;
}

 

komentarz 14 grudnia 2017 przez Knayder Nałogowiec (37,640 p.)

Poczytaj o: Wskaźnik Polimorficzny.
Generalnie chodzi o to

class Base {...}
class Child : public Base {...}

Base* ptr = new Child;

//albo

Child child;
Base* ptr = &child;

 

1 odpowiedź

0 głosów
odpowiedź 14 grudnia 2017 przez Deloryn Bywalec (2,060 p.)
edycja 14 grudnia 2017 przez Deloryn
Hmm, poczytaj może o klasach generycznych (szablonach) i spróbuj utworzyć metodę generyczną atakuj(Obiekt)? A za obiekt mógłby wchodzić i Wilk, i Niedźwiedź. Ale jestem świeżakiem w tym temacie i możliwe, że nie można utworzyć samej metody generycznej (niech inni mnie poprawią).

Możesz również zdefiniować osobno dla Wilka i Niedźwiedzia metody, np. dla Wilka: atakuj(Niedźwiedź), a dla Niedźwiedzia: atakuj(Wilk).
komentarz 14 grudnia 2017 przez Knayder Nałogowiec (37,640 p.)

Można użyć polimorfizmu.

 

Postac::Atakuj(Postac* cel) {
    cel->zadajObrazenia(10);
}

Krowa krowa;
Lis lis;
krowa.Atakuj(&lis);

 

komentarz 15 grudnia 2017 przez MikeMG Początkujący (330 p.)
Wskaźnik polimorficzny znam. Przyznam, że zapisałem to bardzo podobnie, ale coś mi nie działało. Potestuję jeszcze to dzisiaj.
komentarz 15 grudnia 2017 przez MikeMG Początkujący (330 p.)

Wskaźnik polimorficzny znam. Przyznam, że zapisałem to bardzo podobnie, ale coś mi nie działało. Potestuję jeszcze to dzisiaj.

 

EDIT:

No dobrze. Metody mam takie:

    virtual void atakuj(Postac *cel)
    {
        cel->zadanie_obrazen(10)
    }
    virtual void zadanie_obrazen(int dmg)
    {
        hp-=dmg;
    }

Obie metody są składowymi klasy Niedzwiedz i dziedziczą z kl. Postac.
Przeanalizujmy ten kod. Zacznijmy od metody "zadanie_obrazen()":

Metoda otrzymuje inta z ilością zdrowia, które ma sobie zabrać. Przyjmując, że hp to zdrowie - odejmuje je.

"atakuj()":

Wskaźnik polimorficzny wskazuje na cel ataku (inny obiekt), dla atakowanego obiektu wywołuje się funkcja "zadanie_obrazen()" (patrz wyżej).

Niestety dostaję kilka błędów:


In member function 'virtual void Niedzwiedz::atakuj(Postac*)':
error: no matching function for call to 'Postac::zadanie_obrazen(int)'
note: candidate is:
note: virtual void Postac::zadanie_obrazen()
note:   candidate expects 0 arguments, 1 provided|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Wstawiam jeszcze klasę abstrakcyjną:

class Postac
{
public:
    friend int rng(int,int);
    virtual void atakuj()=0;
    virtual void zadanie_obrazen()=0;
    virtual void uciekaj()=0;
};

Reszta jest w poście wyżej.
 

komentarz 15 grudnia 2017 przez Knayder Nałogowiec (37,640 p.)
Nie rozumiem, dlaczego tak bardzo uparłeś się przy tym, żeby Postać była klasa czysto abstrakcyjną. Przecież w każdym ze zwierząt, system zadawania i przyjmowania obrażeń, będzie taki sam. Dam ci przykładowy kod, który pomoże ci zrozumieć:
http://coliru.stacked-crooked.com/a/5d2e9b29004525f8
komentarz 15 grudnia 2017 przez MikeMG Początkujący (330 p.)

Pracowałem trochę przed Twoim komentarzem i Postać nie jest już klasą abstrakcyjną, uparłem się na to, gdyż wpoiłem sobie tak po tutorialu pana Zelenta. :) Teraz w klasie Niedźwiedź i Wilk nie ma żadnych metod. :) Po prostu są one dziedziczone po rodzicu. Niestety, kiedy chcę, aby wilk zaatakował niedźwiedzia, bądź odwrotnie - program się kompiluje, ale nie działa poprawnie.

#include <iostream>
#include <math.h>
#include <windows.h>
#include <cstdlib>
#include <time.h>
#include "rng.h"
using namespace std;

class Postac
{
    string nazwa;
    int hp; // 1 - 100: Zdrowie
    int ap; // 1 - 100: Sila
    int fp; // 1 - 10: Szansa na ucieczke

public:
    friend int rng(int,int);
    virtual void zadanie_obrazen(int dmg)
    {
        hp-=dmg;
        cout<<nazwa<<" "<<dmg<<" obrazen."<<endl;
        if(hp<=0)
        {
            system("cls");
            cout<<"Obiekt "<<nazwa<<" zostal pokonany!"<<endl;
        }
    }
    virtual void atakuj(Postac *cel)
    {
        cel->zadanie_obrazen(rng(ap-2,5));
        cout<<"Obiekt "<<nazwa<<" zadal obiektowi ";
    }

    virtual void uciekaj()
    {
        {
        if(rng(1,10)<=rng(fp-1,3))
        {
            system("cls");
            cout<<"Obiekt "<<nazwa<<" uciekl pomyslnie!"<<endl;
            Sleep(2000);
            exit(0);
        }
        else
        {
            system("cls");
            cout<<"Ucieczka nieudana! (-"<<(10-fp)*5<<" HP)!"<<endl;
            hp-=(10-fp)*5;
            Sleep(2000);
        }
    }
    }

};

class Niedzwiedz :public Postac
{
    string nazwa;
    int hp; // 1 - 100: Zdrowie
    int ap; // 1 - 100: Sila
    int fp; // 1 - 10: Szansa na ucieczke

public:
    friend int rng(int,int);
    Niedzwiedz(string name="Niedzwiedz", int health=100, int attack=15, int flee=4)
    {
        nazwa=name;
        hp=health;
        ap=attack;
        fp=flee;
    }
};
/////////////////////////////////////////
class Wilk :public Postac
{
    string nazwa;
    int hp; // 1 - 100: Zdrowie
    int ap; // 1 - 100: Sila
    int fp; // 1 - 10: Szansa na ucieczke

public:
    friend int rng(int,int);
    Wilk(string name="Wilk", int health=85, int attack=20, int flee=6)
    {
        hp=health;
        ap=attack;
        fp=flee;
    }
};



int main()
{
    srand(time(NULL));

    Wilk w1;
    Niedzwiedz n1;
    Postac *wsk;
    wsk=&w1;
    n1.atakuj(wsk);

    return 0;
}

Podejrzewam, że problem tkwi we wskaźnikach, w metodzie atakuj(), zadanie_obrazen(), bądź wywołaniu w mainie. Może też występować nieodpowiednie poprzesłanianie atrybutów nazwa, bo wyświetla się to:

Obiekt  zostal pokonany!
Obiekt  zadal obiektowi
Process returned 0 (0x0)   execution time : 0.052 s
Press any key to continue.

Z przesłonieniem nie wiem, jak sobie poradzić w metodach atakuj() i zadanie_obrazen(), ponieważ typ (klasa) atakowanego obiektu NIE JEST znana. (Może to być wilk, niedźwiedź, a w przyszłości każda inna klasa, a klasa to własny typ danych.)

1
komentarz 15 grudnia 2017 przez Knayder Nałogowiec (37,640 p.)
Jak dziedziczysz po jakiejś klasie, to dziedziczysz także pola. (hp, ap, fp). Nie musisz ich definiować w każdej następnej klasie. W klasie bazowej postać, daj je w protected, żebyś miał do nich dostęp w klasach pochodnych.
komentarz 15 grudnia 2017 przez MikeMG Początkujący (330 p.)
hp, ap, fp jest praktycznie zawsze przesłaniane, ponieważ wilk jest zwinniejszy od niedźwiedzia, a z kolei ten drugi ma więcej zdrowia. Dlatego są one definiowane od nowa. Tak myślę. Co z tymi wskaźnikami i przesłonięciem? Bo za Chiny nie umiem znaleźć sposobu, który pozwoliłby mi określić, do której klasy się zwracam. Do tej, która atakuje, czy tej, która zostaje zaatakowana. Kompilator najprawdopodobniej też nie wie.

EDIT: Chyba, że użyję preambuły konstruktora. Wtedy konstruktor klasy np. wilk nie będzie potrzebował wewnątrz kl. wilk atrybutów hp, ap, fp, lecz użyje tych z klasy podstawowej. Dobrze myślę?
komentarz 15 grudnia 2017 przez MikeMG Początkujący (330 p.)

No dobrze! Jednak samodzielna analiza w skupieniu czyni cuda! Program ma wskaźnik polimorficzny, konstruktory, listę inicjalizacyjną, dziedziczenie - wszystko działa. Oto ostateczny kod i efekt:

#include <iostream>
#include <math.h>
#include <windows.h>
#include <cstdlib>
#include <time.h>
#include "rng.h"
using namespace std;

class Postac
{
protected:
    string nazwa;
    int hp; // 1 - 100: Zdrowie
    int ap; // 1 - 100: Sila
    int fp; // 1 - 10: Szansa na ucieczke
public:
    Postac(string name="Postac", int health=100, int attack=0, int flee=11)
    {
        nazwa=name;
        hp=health;
        ap=attack;
        fp=flee;
    }
    friend int rng(int,int);
    virtual void zadanie_obrazen(int dmg)
    {
        hp-=dmg;
        cout<<nazwa<<" "<<dmg<<" obrazen."<<endl;
        if(hp<=0)
        {
            system("cls");
            cout<<"Obiekt "<<nazwa<<" zostal pokonany!"<<endl;
        }
    }
    virtual void atakuj(Postac *cel)
    {
        cout<<"Obiekt "<<nazwa<<" zadal obiektowi ";
        cel->zadanie_obrazen(rng(ap-2,5));
    }

    virtual void uciekaj()
    {
        {
            if(rng(1,10)<=rng(fp-1,3))
            {
                system("cls");
                cout<<"Obiekt "<<nazwa<<" uciekl pomyslnie!"<<endl;
                Sleep(2000);
                exit(0);
            }
            else
            {
                system("cls");
                cout<<"Ucieczka nieudana! (-"<<(10-fp)*5<<" HP)!"<<endl;
                hp-=(10-fp)*5;
                Sleep(2000);
            }
        }
    }

};

class Niedzwiedz :public Postac
{
public:
    friend int rng(int,int);
    Niedzwiedz(string name="Niedzwiedz", int health=100, int attack=15, int flee=4)
        :Postac(name, health, attack, flee)
    {
        nazwa=name;
        hp=health;
        ap=attack;
        fp=flee;
    }
};
/////////////////////////////////////////
class Wilk :public Postac
{
public:
    friend int rng(int,int);
    Wilk(string name="Wilk", int health=85, int attack=20, int flee=6)
        :Postac(name, health, attack, flee)
    {
        nazwa=name;
        hp=health;
        ap=attack;
        fp=flee;
    }
};



int main()
{
    srand(time(NULL));

    Wilk w1;
    Niedzwiedz n1;
    Postac *wsk;
    wsk=&w1;
    n1.atakuj(wsk);

    return 0;
}

Wydaje mi się, że kod jest wystarczająco czytelny (w przeciwieństwie do początku mojej przygody z obiektówką). Dziękuję wszystkim za pomoc!

Obiekt Niedzwiedz zadal obiektowi Wilk 17 obrazen.

Process returned 0 (0x0)   execution time : 0.013 s
Press any key to continue.

Podobne pytania

0 głosów
1 odpowiedź 613 wizyt
+5 głosów
3 odpowiedzi 645 wizyt
0 głosów
3 odpowiedzi 428 wizyt
pytanie zadane 28 grudnia 2018 w PHP przez PWD Nowicjusz (160 p.)

92,552 zapytań

141,399 odpowiedzi

319,534 komentarzy

61,938 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...