Tworzenie tablicy obiektów klasy i wypełnianie jej obiektami podklas

0

Witam
Mam sobie trzy klasy, jedną główną i dwie podklasy:

class zwierze{
public
char* nazwa;
void glos()
{
cout << "Sz\n";
}
};

class kot: public zwierze{
public:
char* kolor;
void glos()
{
cout << "Miau\n";
}
};

class pies: public zwierze{
public:
char* rasa;
void glos()
{
cout << "Hau\n";
}
};

I teraz tworze tablice obiektów klasy zwierze

stado= new (nothrow) zwierze[liczba];

I chciałbym wypełnić tą tablice obiektami klasy kot i obiektami klasy pies tak, że jak wywołam po kolei metode glos() dla każdego obiektu z tablicy będzie ona interpretowana zależnie od klasy obiektu. Moje pytanie brzmi jak tego dokonać?
Zrobiłem coś takiego:

	for(int i=0;i<liczba;i++){			
		int temp;
		temp=rand()%1;

		if(temp==0){
		kot* zwierz = new kot();
		zwierz->nazwa=losujImie(rand()%7);
		stado[i]=*zwierz;
		delete zwierz;	
		}
		else
		{
		pies* zwierz = new pies();
		zwierz->nazwa=losujImie(rand()%7);
		stado[i]=*zwierz;
		delete zwierz;		
		}
	}

I niestety działa to tak, że po wywołaniu pętli:

	for(int i=liczba-1;i>=0;i--){

		stado[i].glos();

	}

Dla każdego elementu tablicy wykonywana jest metoda z klasy zwierze.

Jak to rozwiązać?

1

Zacznij od poczytania o metodach wirtualnych.
Jak wypełnisz całą tablicę obiektami kot to każdy obiekt ma ten sam głos.
Może chodziło ci o zwierze *tb[] = { new kot, new pies };
Wtedy każdy ma swój głos (o ile uwzględnisz metody wirtualne).

1

Weź to jakoś sformatuj bo wstyd pokazywać takie coś.

Metody powinny być wirtualne, jeżeli chcesz, żeby to tak działało. Poza tym musisz używać wskaźników albo referencji. zwierze to zawsze ta sama klasa i nic się nie da z tym zrobić.

0

Czyli ma to wygladać tak?

class zwierze{
public
char* nazwa;
virtual void glos()
{
cout << "Sz\n";
}
};
 
class kot: public zwierze{
public:
char* kolor;
void glos()
{
cout << "Miau\n";
}
};
 
class pies: public zwierze{
public:
char* rasa;
void glos()
{
cout << "Hau\n";
}
};

??

0

Przydało by się:

  • jakieś formatowanie kodu
  • jakieś konstruktory
  • powtórzenie klauzuli virtual w klasach potomnych, być nie musi ale ułatwia czytanie kodu.
0
class zwierze{
        public
        char* nazwa;
       zwierze(char*);
       virtual void glos()
                        {
                        cout << "Sz\n";
                        }
};

zwierze::zwierze(char* a)
        {
        nazwa=a
        }
 

class kot: public zwierze{
       public:
      char* kolor;
      virtual void glos()
                     {
                     cout << "Miau\n";
                      }
};
 
class pies: public zwierze{
          public:
          char* rasa;
          virtual void glos()
                           {
                           cout << "Hau\n";
                           }
};

Lepiej?

0

To nie jest formatowanie kodu, formatowanie przede wszystkim polega na jednolitych wcięciach. A co kompilator mówi? Jak może być lepiej jeżeli są błędy?

0

Ogólnie nie miałem błędów w kompilatorze, ale okazało się że to nie działało z tego powodu, że na początku, tak jak napisaliście metoda nie była wirtualna w klasie bazowej. Ale oprócz tego powinien być wskaźnik na wskaźnik typu zwierze wtedy była by to tablica tak na prawdę. Ja ją po prostu źle deklarowałem. Na szczęście sam do tego doszedłem.
Dokładnie o to chodziło:


zwierze** stado;
...
stado= new (nothrow) zwierze*[liczba];
1

Skoro C++ i obiektowość, to może std::string zamiast char*, shared_ptr zamiast gołych wskaźników, std::vector zamiast dynamicznej tablicy?

3

w cpp nie używamy char* do trzymania stringów, jeżeli już to char const*,

przykładowy kontener dla zwierząt i use case dla virtual function

#include <iostream>
#include <memory>
#include <vector>

struct Animal
{
	virtual ~Animal() {}

	virtual void sayYourName() = 0;
};

struct Dog : Animal
{
	virtual void sayYourName() override final { std::cout << "Dog\n"; }
};

struct Lion : Animal
{
	virtual void sayYourName() override final { std::cout << "Lion\n"; }
};

struct Cheetah : Animal
{
	virtual void sayYourName() override final { std::cout << "Cheetah\n"; }
};

int main()
{
	std::vector<std::unique_ptr<Animal>> animals;
	animals.push_back(std::make_unique<Lion>());
	animals.push_back(std::make_unique<Cheetah>());
	animals.push_back(std::make_unique<Dog>());

	for(auto const& animal : animals) {
		animal->sayYourName();
	}

	return 0;
}

http://melpon.org/wandbox

0
twonek napisał(a):

Skoro C++ i obiektowość, to może std::string zamiast char*, shared_ptr zamiast gołych wskaźników, std::vector zamiast dynamicznej tablicy?

Mam funkcje losującą nazwę, w której miałem problem ze stringiem więc użyłem char* . Jeśli mam być szczerzy to nie słyszałem nigdy o "shared_ptr", a co do "std::vector" do końca nie wiem jak się tym obsługiwać.

Ale mam jeszcze pytanie. Bo nigdzie nie mogę tego znaleźć.

W jaki sposób wywołać kostruktor dla każdej z klas. Bo używając metody tak jak poniżej dla każdej klasy. Nie chce się skompilować. Czytałem coś, że trzeba użyć konstruktor kopiującego, ale nie za bardzo rozumiem jak on działa. Chociaż czytałem troche na jego temat.

class zwierze{
public
char* nazwa;
zwierze(char*);
virtual void glos()
{
cout << "Sz\n";
}

zwierze::zwierze(char* a)
{
nazwa=a
}
0
  1. Zanim zaczniesz pisać dalej poczytaj o formatowaniu, bez formatowania nikomu nie chce się w tym g*nie się babrać, nawet tobie
  2. Używaj listy inicjalizacyjnej
  3. Używaj rozsądnych inline'owań
  4. Używaj endl zamiast "\n"
  5. Konstruktor klasy pochodnej musi użyć jednego konstruktora z klasy bazowej (no chyba że bazowa ma konstruktor domyślny)
  6. Nie przypisuj bezpośrednio char * do składowych klasy, zrób kopię
  7. Jeżeli masz klasę bazową to koniecznie dodaj wirtualny destruktor
  8. Do każdej klasy przydzielającej pamięć koniecznie trzeba dodać konstruktor przenoszący,konstruktor kopiujący, operator przepisania
  9. Zastanów się czy w związku z powyższym nie zrezygnować jednak z char * na rzecz std::string
class zwierze
  {
   private:
   char *nazwa;
   protected:
   static char *StrDup(const char *str);
     {
      size_t mem=strlen(str)+1;
      char *tmp=new char[mem];
      memcpy(tmp,str,mem);
      return tmp;
     }
   public:
   zwierze(const char *nazwa):nazwa(StrDup(nazwa)) {}
   virtual ~zwierze() { delete[] nazwa; }
   virtual void glos() { cout<<"Sz"<<endl; }
  }; 

class  kot:public zwierze
  {
   private:
   char *kolor;
   public:
   kot(const char *nazwa,const char *kolor):zwierze(nazwa),kolor(StrDup(kolor)) {}
   ~kot() { delete[] kolor; }
   virtual void glos() { cout<<"Miau"<<endl; }
  };
0
_13th_Dragon napisał(a):

virtual ~zwierze() { delete[] nazwa; }



Ten fragment nie działa u mnie poprawnie. Co prawda się kompiluje, ale w momencie wykonania crashuje aplikacje.
Z tego co wyczytałem to destruktor jest wykonywany w chwili usuwania obietku, jak mogłoby być możliwe usunięcię usuniętego już elementu? Zrobiłem test i zmieniłem instrukcje w tej funkcji na 
```cpp
 cout << "Usunieto" <<  endl; <

i w momencie wykonywania obiektu typu zwierzedelete zwierz;

 i moja instrukcja z destruktora była wykonywana. Wobec tego mógłby mi ktoś wyjaśnić w jaki sposób to działa?
0

@Aftermathx25 Destruktor jest wywołany tuż przed fizycznym usunięciem obiektu z pamięci. Jego zadaniem jest sprzątanie (usuwanie, ustawianie pewnych wartości) pól składowych obiektu, nie usuwa samego obiektu (cokolwiek to znaczy). Zauważ, że jeśli wszystkie pola to typy proste (int, double itd.) to destruktor nie musi nic robić. Robotę ma dopiero gdy masz coś tworzonego np. na stercie jak char* w Twoim przykładzie, bo nawet jeśli wskaźnik zostaje usuwany automatycznie, to ta zaalokowana pamięć na ten napis nie zostaje zwolniona bez delete[].
W destruktorze może się wykonać dowolny kod, więc jest wykorzystany również do innych rzeczy, patrz RAII.

Jeśli delete[] nazwa Ci się crashuje, to pewnie gdzieś wcześniej zepsułeś ten wskaźnik (zwolniłeś już pamięć pod tym adresem).

0
Aftermathx25 napisał(a):
_13th_Dragon napisał(a):

virtual ~zwierze() { delete[] nazwa; }

> 
> 
> Ten fragment nie działa u mnie poprawnie.
 Na to jest tylko dwa wytłumaczenia:
1. Nie umiesz skopiować
2. Nie umiesz użyć

http://ideone.com/KM5f7f

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