Problem z przeciążeniem operatorów

0

Witam,
otóż mam 3 klasy: klasę bazową "Obiekt" jej klasę pochodną "Gwiazda" i klasę "Galaktyka" mam przeciążyć operator strumienia oraz dodawania i usuwania obiektów (wspomnianych już gwiazd, później dodam kwazary, czarne dziury etc) do/z Galaktyki (wiem, łatwiej byłoby zrobić to za pomocą metod, ale taka jest część mojego zadania). To co zrobiłem do tej pory:

#include <iostream>
using namespace std;
 
class Obiekt{
private:
        string nazwa;
public:
        Obiekt(string we) : nazwa(we){}
        string& jaka_nazwa(){ return nazwa; }
 
        virtual void ktos_ty() = 0;
};
 
class Gwiazda : public Obiekt{
public:
        Gwiazda(string we) : Obiekt(we){}
         
        void ktos_ty(){ cout << jaka_nazwa() << "(gwiazda)" << endl; }
};
 
class Galaktyka{
private:
        string nazwa;
        int rozmiar;
        int licznosc;
        Obiekt** tab;
public:
        Galaktyka(string nz, int r) : nazwa(nz), rozmiar(r), licznosc(0){
                tab = new Obiekt*[rozmiar];
        }
 
        ~Galaktyka(){
                for(int i = 0; i < rozmiar; i++) delete tab[i];
                delete[] tab;
        }
//
        friend Galaktyka operator + (Gwiazda* gw, Galaktyka g) {
                 if(g.licznosc < g.rozmiar){
                          g.tab[g.licznosc++] = gw; 
                 }
                 else {
                          cout<< "Galaktyka pełna" <<endl;
                 }
        }
 
        friend ostream& operator << (ostream& out, const Galaktyka &g)
        {
                cout << g.nazwa << ":\n";
                for(int i = 0; i < g.rozmiar; i++) g.tab[i]->ktos_ty();
        }
 
};
 
int main(int argc, char * const argv[]) {
       Galaktyka orion ("Orion", 15);
       Gwiazda alpha("Alpha Centauri");
}

Nie mam pojęcia jednak jak mam poprawnie wywołać moje przeciążone dodawanie. Próbowałem dwóch potworków:

orion + alpha; //no match for 'operator+' in 'orion + alpha'
orion& + (aplha, orion); // no match for 'operator+' in '+(0, orion)'

Które jak widać nie zadziałały. Ma ktoś jakiś pomysł? Z góry dziękuję za wszelką pomoc!

1
const Galaktyka& operator += (const Galaktyka& g) { // wywalasz słowo friend i parametry tak jak pokazałem, najlepiej jakbyś tutaj użył operatora += zamiast +
                 if(licznosc < rozmiar){
                          tab[licznosc++] = &g; 
                 }
                 else {
                          cout<< "Galaktyka pełna" <<endl;
                 }
}

swoją drogą, dopóki nie zdefiniujesz konstruktora kopiującego to będziesz miał cały czas problemy. dodatkowo:

friend ostream& operator << (ostream& out, const Galaktyka &g); // w klasie

// poza klasa:
ostream& operator << (ostream& out, const Galaktyka &g);
{
                cout << g.nazwa << ":\n";
                for(int i = 0; i < g.rozmiar; i++) g.tab[i]->ktos_ty();
}
0

Dzięki za odpowiedź!
Zmieniłem operator na += według twoich wskazówek, ale kompilator wyrzuca mi błąd:

tab[licznosc++] = &g; // cannot convert 'const Galaktyka*' to 'Obiekt*' in assigment 

który jest spowodowany tym, że (jak dobrze rozumiem) w twojej wersji operatora, dodajemy do siebie Galaktyki - a mi chodziło o to, aby do Galaktyki dodawać jakieś Obiekty, np Gwiazdy.

Konstruktor kopiujący będzie wyglądał tak:

 
Galaktyka( const Galaktyka &galaktyka); 

?

0

@_13th_Dragon - err...tu masz kompletną rację. W sumie to nie wiem jak ja na to od razu nie wpadłem...

Mam dwa nowe problemy:

UNO - przeciążyłem operator -= do usuwania obiektów z Galaktyk:

Galaktyka & operator -= ( Obiekt* ob){ delete ob; }

Jak widać poszedłem po linii najmniejszego oporu. Kwestia pierwsza, kompilator nie zgłasza żadnych problemów, ale nie jestem przekonany czy nie powinno się tego jakoś lepiej napisać. Druga kwestia, próba wywołania mojego potworka kończy się niepowodzeniem:

Gwiazda blabla ("Blabla");
orion -= blabla; // no match for 'operator-=' in 'orion -= blabla'
orion -= (blabla*); //expected primary-expression before ')' token

Pewnie to znów jakaś oczywistość, ale ja już nie mam pomysłów.

DUO - Po wypisaniu listy obiektów przez operator strumienia dochodzi do Segmentation fault, czyli występuje problem z pamięcią, który nie wiem jak rozwiąć. Próbowałem to naprawić przez dopisanie return out; ale to nie był dobry pomysł.

        friend ostream& operator << (ostream& out, const Galaktyka &g)
        {
                cout << g.nazwa << ":\n";
                for(int i = 0; i < g.rozmiar; i++) g.tab[i]->ktos_ty();
                //return out; //dodanie tego zamienia segfault na wypis stanu pamięci, jeśli dobrze to rozumiem
        }
0
Gwiazda blabla ("Blabla");
orion -= &blabla; // Obiekt* podałeś jako parametr

To jest źle

Galaktyka & operator -= (Obiekt* ob){ delete ob; }

musisz w pętli przeszukać tablice w galaktyce i żadnego delete chyba ze dodajesz zawsze tak:

Galaktyka G;
G+=new Gwiazda("Gw1234");

A skoro nie możesz tego dopilnować czyli ktoś może zrobić:

Galaktyka G; Gwiazda Gw("Gw1234");
G+=&Gw;
G-=&Gw; // to zrobi kuku
0

@_13th_Dragon Czyli to by było mniej więcej tak:

Galaktyka & operator -= (Obiekt* ob){
   for (int i=0; i < rozmiar; i++){ //przeszukuje całą tablicę
        if ( tab[i] = ob){ // glibc detected, muszę przeciążyć operator = aby to działało poprawnie, zgadza się?
              delete ob; // w przypadku gdy zawsze konstruuje z new dodawane obiekty, a w innym przypadku to...well, na pewno nie mogę wywołać destruktora Obiektu, tyle to wiem
        }
   }
}
0
Galaktyka &operator-=(Obiekt *ob)
  {
   for(int i=0;i<rozmiar;++i) // nie korzystaj z i++
     {
      if(tab[i]==ob) // pojedyncze = to przepisanie
        {
         tab[i]=tab[--rozmiar]; // ostatni wpisać w to miejsce (ewentualnie wszystko po przesuwać) oraz koniecznie zmniejszyć ilość
         delete ob; // odradzam bo tego nie upilnujesz.
         break; // nie ma co dalej szukać
        }
     }
  }
0

@_13th_Dragon, dzięki wielkie za pomoc. Stworzyłem takiego działającego potworka:

	Galaktyka & operator -= ( Obiekt* ob) { //czyli wywołuję za pomocą JakaśGalaktyka += &JakaśGwiazda;
		for (int i=0; i < rozmiar; ++i) {
			if ( tab[i]==ob) {
				tab[i]=tab[--rozmiar]; 
				--rozmiar;
				++rozmiar; //- i + rozmiar dzięki czemu pożądany ob zostaje usunięty z Galaktyki
				break;
			}
		}
	}

Został ostatni problem, segfault pojawiający się zaraz po wypisaniu przez operator przypisania wszystkich Obiektów w Galaktyce. Myślałem, że rozwiązaniem będzie dodatkowa pętla if
Kod stary:

ostream& operator << (ostream& out, const Galaktyka &g)
{
	cout << g.nazwa << ":\n";
	for(int i = 0; i < g.rozmiar; i++) g.tab[i]->ktos_ty();
}

Kod nowy:

ostream& operator << (ostream& out, const Galaktyka& g)
{
	cout << g.nazwa << ":\n";
	for(int i = 0; i < g.rozmiar; ++i) {
		if (g.tab[i]==0) break;
		else g.tab[i]->ktos_ty();
	}
}

Ale albo to nic to nie zmienia albo nie dokońca dobrze to robię.

0
  1. Funkcja ktos_ty miesza „logikę biznesową” z warstwą prezentacji. Prostszymi słowy: w porządku że jest funkcja jaka_nazwa zwracająca stringa, ale już nie w porządku że jest funkcja ktos_ty która tę nazwę wyświetla. Wyświetlanie powinno być albo w całości w operatorze << Galaktyki, albo każda Gwiazda będzie miała własny operator <<, użyty w Galaktyce.

  2. Operator << powinien pisać do strumienia podanego w argumencie (u ciebie nazywającego się out) a nie do cout.
    2a. Teraz widzisz dlaczego funkcja ktos_ty jest bez sensu: ponieważ pisze na sztywno do cout, a powinna do strumienia podanego w operatorze.

  3. operator << u ciebie zwraca ostream& a nie ma żadnego return. Operator powinien zwracać strumień, czyli return out;

0

@Azarien, zmieniłem funkcje ktos_ty aby zwracał stringa. Cały kod:

#include <iostream>
#include <string>
using namespace std;

class Obiekt{
private:
	string nazwa;
public:
	string a;
	Obiekt(string we) : nazwa(we){}
	string& jaka_nazwa(){ return nazwa; }

	virtual string& ktos_ty(){return a=(jaka_nazwa() + "(obiekt kosmiczny)"); }
};

class Gwiazda : public Obiekt{
public:
	char klasa; // klasa jasnosci gwiazdy, od najjasniejszej - O B A F G K M

	Gwiazda(string we, char k) : Obiekt(we), klasa(k){}
	
	string& ktos_ty(){ return a=(jaka_nazwa() + "(gwiazda, o klasie jasnosci " + klasa + " )");}
};

class Kwazar : public Obiekt{
	
public:
	Kwazar(string we) : Obiekt(we){}

	
//	string& ktos_ty(){ out << jaka_nazwa() << "(kwazar)" << endl; }
};

class CzarnaDziura : public Obiekt{

public:
	CzarnaDziura(string we) : Obiekt(we) {}

//	ostream& ktos_ty(ostream& out) {out << jaka_nazwa() << "(czarna dziura)" << endl; }

};

class Konstelacja : public Obiekt{
	int iloscG; // ilosc gwiazd w konstelacji
public:
	Konstelacja(string we, int i) : Obiekt(we), iloscG(i) {}

//	ostream& ktos_ty(ostream& out) {out << jaka_nazwa() << "(konstelacja zlozona z " << iloscG <<  " gwiazd)" << endl; }

};

class Gwiazdozbior{
private:
	string nazwa;
	int rozmiar;
	int licznosc;
	Gwiazda** tab;
public:
	Gwiazdozbior(string nz, int r) : nazwa(nz), rozmiar(r), licznosc(0){
		tab = new Gwiazda*[rozmiar];
	}

	~Gwiazdozbior(){
		for(int i = 0; i < licznosc; i++) delete tab[i];
		delete[] tab;
	}
	
	const Gwiazdozbior & operator += (Gwiazda& g) {
		if(licznosc < rozmiar){
			tab[licznosc++] = &g;
		}
		else {
			cout<< "Gwiazdozbior pełen" <<endl;
		}
	}
/*	void print(){
		out << nazwa << ":\n";
		for(int i = 0; i < licznosc; i++) tab[i]->ktos_ty(out);
	}

	friend ostream& operator << (ostream& out, Gwiazdozbior& g){g.print();}
*/
};

//ostream& operator << (ostream& out, const Gwiazdozbior& g){g.print();}



class Galaktyka{
private:
	string nazwa;
	int rozmiar;
	int licznosc;
	Obiekt** tab;
public:
	Galaktyka(string nz, int r) : nazwa(nz), rozmiar(r), licznosc(0){
		tab = new Obiekt*[rozmiar];
	}

	Galaktyka(const Galaktyka &galaktyka);

	~Galaktyka(){
		for(int i = 0; i < rozmiar; i++) delete tab[i];
		delete[] tab;
	}

	const Galaktyka & operator += ( Obiekt& g) {
		if(licznosc < rozmiar){
			tab[licznosc++] = &g;
		}
		else {
			cout<< "Galaktyka pełna" <<endl;
		}
	}
	
	Galaktyka & operator -= ( Obiekt* ob) {
		for (int i=0; i < rozmiar; ++i) {
			if ( tab[i]==ob) {
				tab[i]=tab[--rozmiar]; 
				--rozmiar;
				++rozmiar;
				break;
			}
		}
	}

	friend ostream& operator << (ostream& out, const Galaktyka &g);
};

ostream& operator << (ostream& out, const Galaktyka& g)
{
	out << g.nazwa << ":\n";
	for(int i = 0; i < g.rozmiar; ++i)
		out << g.tab[i]->ktos_ty() << endl;
	return out;
}

int main(int argc, char *  argv[]) {

	Galaktyka orion ("Orion", 3);
	Gwiazda alpha ("Alpha Centauri", 'B');
	Gwiazda blabla ("Blabla", 'C');
	orion += alpha;
	orion += blabla;
	Gwiazda slonice("Słońce", 'A');
	orion += slonice;
	orion -= &slonice;	
	cout << orion << endl;
	return 0;
}

Kompiluje się wszystko, ale w konsoli pojawia się:

Orion:
Alpha Centauri(gwiazda, o klasa jasności B )
Blabla(gwiazda, o klasie jasności C )
//czyli zadziałało wszystko zgodnie z zamysłem, ale pojawia się
*** glibc detected ** ./main double free or corruption (fasttop): 0x0000000000a351c0 ***
//i dużo tekstu, który chyba nie jest ważny

Coś jest nie tak z pamięcią. Hmm... program próbuje jakąś przypisaną pamięć kasować dwukrotnie?

0

string& jaka_nazwa(){ return nazwa; }
virtual string& ktos_ty(){return a=(jaka_nazwa() + "(obiekt kosmiczny)"); }
po co ci dwie funkcje? zostaw tylko jaka_nazwa().

ostream& operator << (ostream& out, const Galaktyka& g)
{
        out << g.nazwa << ":\n";
        for(int i = 0; i < g.rozmiar; ++i)
                out << g.tab[i]->ktos_ty() << endl;
        return out;
}

no już lepiej.

jeszcze lepiej będzie tak:

        out << g.nazwa << ":\n";
        for(int i = 0; i < g.rozmiar; ++i)
                out << *g.tab[i] << endl;
        return out;

i dodatkowo drugi operator dla samej klasy Obiekt.

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