C++ Builder znikające obiekty - problem

0

titleCzesc,

piszę prosty program w C++ Builder,. Program to symulacja zagrody z owcami i wpuszczamy do niej wilka, który zjada napotkane owce. Mam problem z tym, że nie działają linie odpowiadające za znikanie owiec. Dopiero uczę się uczę się wykorzystywania wskaźników i funkcji wirtualnych. Z góry dziękuję bardzo za pomoc!

Wilk - duży obiekt typu TShape, kwadrat
Owca- mały obiekt typu TShape, kwadrat


#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"


#define Sx 400
#define Sy 300
#define N 50
bool spotkanie(TShape *owca, TShape *wilk)
{
     if (owca->Left >= wilk->Left-owca->Width &&
        owca->Left <= wilk->Left+owca->Width &&
        owca->Top >= wilk->Top-owca->Width &&
        owca->Top >= wilk->Top+owca->Width)
        {
                return true;
        }
        else return false;

}

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{

}
//---------------------------------------------------------------------------



class Zwierze //klasa abstrakcyjna
  {
  public:
  int x0,y0,  vx,vy;
  double masa,q;


  virtual void ruch() =0;
  virtual void smierc()=0;

  };
  //KLASA OWCA////////////////////////////////////////
  class Owca:public Zwierze
  {
        public:
        TShape *ksz_o;
        virtual void ruch()
      {
      x0+=vx;
      y0+=vy;
      if(x0>=Sx) { vx=-vx; x0=Sx-1; }
      if(x0<0)   { vx=-vx; x0=0;    }
      if(y0>=Sy) { vy=-vy; y0=Sy-1; }
      if(y0<0)   { vy=-vy; y0=0;    }
      ksz_o->Left=x0;
      ksz_o->Top=y0;
      }
      virtual void smierc() //ginie gdy trafi na wilka
      {
          if(spotkanie(ksz_o, ksz_w))ksz_o->Visible=false; // znikanie kwadracika(owcy), gdy napotka wilka
      }
      Owca()     //konstruktor owcy
      {
      x0=random(Sx);
      y0=random(Sy);
      while( (vx=random(7)-3)==0);
      while( (vy=random(7)-3)==0);

      ksz_o=new TShape(Form1);
      ksz_o->Parent=Form1;
      ksz_o->Left=x0;
      ksz_o->Top=y0;
      ksz_o->Width=10;
      ksz_o->Height=10;
      ksz_o->Visible=true;
      ksz_o->Shape=stSquare;
      }
  ~Owca(void);//
  };

  //KLASA WILK////////////////////////////////////////
  class Wilk:public Zwierze
  {

        public:
        TShape *ksz_w;
        virtual void ruch()
      {
      x0+=vx;
      y0+=vy;
      if(x0>=Sx) { vx=-vx; x0=Sx-1; }
      if(x0<0)   { vx=-vx; x0=0;    }
      if(y0>=Sy) { vy=-vy; y0=Sy-1; }
      if(y0<0)   { vy=-vy; y0=0;    }
      ksz_w->Left=x0;
      ksz_w->Top=y0;
      }
      virtual void smierc(void);
      Wilk()     //konstruktor wilka
      {
      x0=random(Sx);
      y0=random(Sy);
      vx=20;
      vy=20;

      ksz_w=new TShape(Form1);
      ksz_w->Parent=Form1;
      ksz_w->Left=x0;
      ksz_w->Top=y0;
      ksz_w->Width=50;
      ksz_w->Height=50;
      ksz_w->Visible=true;
      ksz_w->Shape=stSquare;
      }
  ~Wilk(void);
  };


Owca *to;
Wilk *tw;

  Owca::~Owca()
      {
      }

  Wilk::~Wilk()
      {
      }


        void Wilk::smierc()
        {

        }





void __fastcall TForm1::Timer1Timer(TObject *Sender)
{

tw->ruch();
tw->smierc();
to->ruch();
to->smierc();



}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
tw = new Wilk;
to = new Owca;

Timer1->Enabled = 1;

}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------

screenshot-20200608122418.png

0
if(spotkanie(ksz_o, ksz_w))ksz_o->Visible=false;

Czy zmienna z klasy Wilk ksz_w jest widoczna w miejscu wywołania metody smierc z klasy Owca?

0

Możesz dodać publiczną metodę tryEatSheep do klasy wilka.

bool tryEatSheep( Owca *owca )
{     
    if( spotkanie(owca->ksz_o, ksz_w) ) owca->ksz_o->Visible=false;
}

Ale tak na prawdę, najlepszym rozwiązaniem będzie gruntowna przebudowa aplikacji w której:

  1. Unikaj zmiennych i funkcji globalnych.
  2. Nie używaj gołych wskaźników, a jeżeli musisz to przynajmniej usuwaj obiekty powstałe za ich pomocą ( RAII )
  3. Destruktor klasy bazowej powinien być wirtualny.
  4. Nie używaj własnych makr.
  5. Nadaj nazwą metod jakąś sensowne określenie. Na przykład nazwa smierc nic nie mówi na temat tego co dzieje się w środku? To może być wszystko, od nieprzyjemnego zapachu do przejechania zwierzęcia przez samochód.
0

Niestety nie działa. Wilk nawet nie rusza. Może zliczanie masy jest w złym miejscu?

0

Konstruktor klasy Wilka musi ustawiać jego masę tzn. dodaj masa_wilka=0; w konstruktorze.
Czy przypadkiem nie robisz tego w funkcji ruch()?

0

Racja robilem to w funkcji ruch(). Teraz kod wyglada tak, jednak wilk zatrzymuje sie po zjedzeniu juz 1 owcy.

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"


#define Sx 500
#define Sy 500
#define N 5
bool spotkanie_owieczki(TImage *owca, TImage *wilk)
{
     if (owca->Left >= wilk->Left-owca->Width &&
        owca->Left <= wilk->Left+wilk->Width &&
        owca->Top >= wilk->Top-owca->Height &&
        owca->Top <= wilk->Top+wilk->Height)
        {
                return true;
        }
        else return false;

}

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{

}
//---------------------------------------------------------------------------



class Zwierze //klasa abstrakcyjna
  {
  public:
  int x0,y0,  vx,vy;
  int masa,q;

  virtual void ruch() =0;

  };

  //KLASA OWCA////////////////////////////////////////
  class Owca:public Zwierze
  {
        public:
        TImage *ksz_o;
        virtual void ruch()
      {
      x0+=vx;
      y0+=vy;
      if(x0>=Sx) { vx=-vx; x0=Sx-1; }
      if(x0<0)   { vx=-vx; x0=0;    }
      if(y0>=Sy) { vy=-vy; y0=Sy-1; }
      if(y0<0)   { vy=-vy; y0=0;    }
      ksz_o->Left=x0;
      ksz_o->Top=y0;
      }
      Owca();     //konstruktor owcy
      ~Owca(void);//
  };

  Owca::Owca()     //konstruktor owcy - stworzenie ksztaltu i przypisanie wartosci poczatkowych
      {
      x0=random(Sx);
      y0=random(Sy);
      while( (vx=random(7)-3)==0);
      while( (vy=random(7)-3)==0);
      //stwarza ksztalt
      ksz_o=new TImage(Form1);
      ksz_o->Parent=Form1;
      ksz_o->Left=x0;
      ksz_o->Top=y0;
      ksz_o->Width=100;
      ksz_o->Height=100;
      ksz_o->AutoSize=true;
      ksz_o->Visible=true;
      ksz_o->Picture->LoadFromFile("img/sheep.bmp");
      }

  //KLASA WILK////////////////////////////////////////
  class Wilk :public Zwierze
  {
        public:
        TImage *ksz_w;
        int masa_wilka;
        virtual void ruch()
      {
      x0+=vx;
      y0+=vy;
      if(x0>=Sx) { vx=-vx; x0=Sx-1; }
      if(x0<0)   { vx=-vx; x0=0;    }
      if(y0>=Sy) { vy=-vy; y0=Sy-1; }
      if(y0<0)   { vy=-vy; y0=0;    }
      ksz_w->Left=x0;
      ksz_w->Top=y0;
      }



      int zjedzOwce( Owca *owca )    //funkcja, ktora sprawdza czy wilk napotkal owieczke, jesli tak to ja zjada
        {
                if(spotkanie_owieczki(owca->ksz_o, ksz_w) )
                        {
                                owca->ksz_o->Visible=false;
                                masa_wilka+=1;      // gdy wilk zje owce masa wzrasta o 1
                                return masa_wilka;
                        }

        }


        Wilk();     //konstruktor wilka
        ~Wilk(void);
  };

     Wilk::Wilk()     //konstruktor wilka - stworzenie ksztaltu i przypisanie wartosci poczatkowych
      {
      x0=random(Sx);
      y0=random(Sy);
      vx=20;
      vy=20;
      masa_wilka=0;

      //stwarza ksztalt
      ksz_w=new TImage(Form1);
      ksz_w->Parent=Form1;
      ksz_w->Left=x0;
      ksz_w->Top=y0;
      ksz_w->Width=60;
      ksz_w->Height=60;
      ksz_w->Visible=true;
      ksz_w->AutoSize=true;
      ksz_w->Picture->LoadFromFile("img/wolf.bmp");
      }

        Owca *to[N];
        Wilk *tw;

  Owca::~Owca()
      {
        delete [] to;
      }

  Wilk::~Wilk()
      {
        delete [] tw;
      }


void __fastcall TForm1::Timer1Timer(TObject *Sender)
{


        int i;
        for(i=0; i<N;i++)
        {
          to[i]->ruch();
          tw->zjedzOwce(to[i]);

        }


        if(tw-> masa_wilka<3)tw->ruch(); //ruszaj wilkiem dopoki nie zje 3 owiec





}
//---------------------------------------------------------------------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{
        int i;
        for(i=0;i<N;++i) to[i]= new Owca;
        tw= new Wilk;
        Timer1->Enabled = 1;

}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------





0

Dodaj warunek sprawdzający czy owca, którą chce pożreć wilk, nie była już wcześniej pożarta.

 if(  owca->ksz_o->Visible == true  &&  spotkanie_owieczki(owca->ksz_o, ksz_w) )
0

Męczę się jeszcze z jedną rzeczą. Po drugim wciśnięciu przycisku stwarzającego obiektu, chciałbym zresetować symulację i wykasować poprzednie obiekty z planszy.

Kod wygląda tak i polecenia delete nie działają.

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"


#define Sx 500
#define Sy 500
#define N 8
bool spotkanie_owieczki(TImage *owca, TImage *wilk)
{
     if (owca->Left >= wilk->Left-owca->Width &&
        owca->Left <= wilk->Left+wilk->Width &&
        owca->Top >= wilk->Top-owca->Height &&
        owca->Top <= wilk->Top+wilk->Height)
        {
                return true;
        }
        else return false;

}

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{

}
//---------------------------------------------------------------------------



class Zwierze //klasa abstrakcyjna
  {
  public:
  int x0,y0,  vx,vy;
  int masa,q;

  virtual void ruch() =0;

  };

  //KLASA OWCA////////////////////////////////////////
  class Owca:public Zwierze
  {
        public:
        TImage *ksz_o;
        virtual void ruch()
      {
      x0+=vx;
      y0+=vy;
      if(x0>=Sx) { vx=-vx; x0=Sx-1; }
      if(x0<0)   { vx=-vx; x0=0;    }
      if(y0>=Sy) { vy=-vy; y0=Sy-1; }
      if(y0<0)   { vy=-vy; y0=0;    }
      ksz_o->Left=x0;
      ksz_o->Top=y0;
      }
      Owca();     //konstruktor owcy
      ~Owca(void);//
  };

  Owca::Owca()     //konstruktor owcy - stworzenie ksztaltu i przypisanie wartosci poczatkowych
      {
      x0=random(Sx);
      y0=random(Sy);
      while( (vx=random(7)-3)==0);
      while( (vy=random(7)-3)==0);
      //stwarza ksztalt
      ksz_o=new TImage(Form1);
      ksz_o->Parent=Form1;
      ksz_o->Left=x0;
      ksz_o->Top=y0;
      ksz_o->Width=100;
      ksz_o->Height=100;
      ksz_o->AutoSize=true;
      ksz_o->Visible=true;
      ksz_o->Picture->LoadFromFile("img/sheep.bmp");
      }

  //KLASA WILK////////////////////////////////////////
  class Wilk :public Zwierze
  {
        public:
        TImage *ksz_w;
        int masa_wilka;
        virtual void ruch()
      {
      x0+=vx;
      y0+=vy;
      if(x0>=Sx) { vx=-vx; x0=Sx-1; }
      if(x0<0)   { vx=-vx; x0=0;    }
      if(y0>=Sy) { vy=-vy; y0=Sy-1; }
      if(y0<0)   { vy=-vy; y0=0;    }
      ksz_w->Left=x0;
      ksz_w->Top=y0;
      }



      int zjedzOwce( Owca *owca )    //funkcja, ktora sprawdza czy wilk napotkal owieczke, jesli tak to ja zjada
        {
                 if(  owca->ksz_o->Visible == true  &&  spotkanie_owieczki(owca->ksz_o, ksz_w) )
                        {
                                owca->ksz_o->Visible=false;
                                masa_wilka+=1;      // gdy wilk zje owce masa wzrasta o 1
                                return masa_wilka;
                        }

        }


        Wilk();     //konstruktor wilka
        ~Wilk(void);
  };

     Wilk::Wilk()     //konstruktor wilka - stworzenie ksztaltu i przypisanie wartosci poczatkowych
      {
      x0=random(Sx);
      y0=random(Sy);
      vx=20;
      vy=20;
      masa_wilka=0;

      //stwarza ksztalt
      ksz_w=new TImage(Form1);
      ksz_w->Parent=Form1;
      ksz_w->Left=x0;
      ksz_w->Top=y0;
      ksz_w->Width=60;
      ksz_w->Height=60;
      ksz_w->Visible=true;
      ksz_w->AutoSize=true;
      ksz_w->Picture->LoadFromFile("img/wolf.bmp");
      }

        Owca *to[N];
        Wilk *tw;

  Owca::~Owca()
      {

      }

  Wilk::~Wilk()
      {

      }


void __fastcall TForm1::Timer1Timer(TObject *Sender)
{

        if(tw->masa_wilka<5)
        {
        tw->ruch(); //ruszaj wilkiem dopoki nie zje 3 owiec
        }
        int i;
        for(i=0; i<N;i++)
        {
          to[i]->ruch();
          tw->zjedzOwce(to[i]);

        }







}
//---------------------------------------------------------------------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{
        int i;
        delete tw;
        for(i=0;i<N;++i) delete to[i];


        for(i=0;i<N;++i) to[i]= new Owca;
        tw= new Wilk;
        Timer1->Enabled = 1;

}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------






screenshot-20200610165525.png

1

Każdy obiekt utworzony za pomocą new, musi być w którymś momencie usunięty za pomocą delete. Jeśli tego nie zrobisz doprowadzisz do wycieku pamięci. Natomiast to co robisz w funkcji Button2Click jest jeszcze bardziej niebezpieczne. Próbujesz usuwać obiekty, które nie zostały jeszcze utworzone (wskaźniki do nich są nie-zainicjalizowane) i zamiast tego dostajesz UB.

Jeżeli mam być szczery, to dalsze rozwijanie programu w takiej formie jak jest obecnie, gdzie gołe wskaźniki sterczą z kodu niczym kości w otwartym złamaniu, jest IMHO porywaniem się z motyką na słońce.
Spróbuj zamiast gołych wskaźników użyć kontenera std::vector.

     std::vector<Owca> to(N);
     std::vector<Wilk> tw(1);

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