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

question-closed Projektowanie klasy abstrakcyjnej, problem z wirtualnym przciążaniem operatora<<

+1 głos
479 wizyt
pytanie zadane 3 czerwca 2018 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 3 czerwca 2018 przez Jakub 0

Hej, ćwicząc nowo nabywaną wiedzę zacząłem projektować prostą klasę bazową o nazwie BaseRectangle, z niej dziedziczy klasa opisująca kwadrat oraz opisująca prostokąt. Dla klas potomnych chciałem przeciążyć operator<<

( jego definicja wygląda inaczej dla dwóch klas dziedziczących ), problem jest jednak taki że podany operator nie powinna przeciążać metoda lecz zwykła funkcja z pierwszy argumentem jako referencja na obiekt klasy std::ostream a z drugim jako np. stała referencja na nasz obiekt. Wobec tego funkcja operatora<< wywołuje odpowiednią metodę wirtualną ( przeciążającą << ) w zależności od podanego w argumencie obiektu dziedziczącego po BaseRectangle. Wszystko wygląda tak:

#include <iostream>
#include <string>

class BaseRectangle;
std::ostream& operator<<(std::ostream&, const BaseRectangle&); //drugi parametr to 
//referencja typu klasy abstrakcyjnej

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

class BaseRectangle { //klasa abstrakcyjna
	friend std::ostream& operator<<(std::ostream&, const BaseRectangle&);
public:
	struct Coords { int x; int y; };
private:
	Coords pos;
public:

	BaseRectangle(int pos_x=0, int pos_y=0) : pos{ pos_x,pos_y } {}

	Coords& returnPos() {
		return pos;
	}

	Coords returnPos() const {
		return pos;
	}

	void move(int x, int y) {
		pos = { x,y };
	}

	virtual long area() = 0;

protected:
	virtual std::ostream& operator<<(std::ostream& os) const = 0; //by klasy potomne wiedzialy o istnieniu tej metody i mogły mieć jej 
//własną definicje ale by reszta programu nie miała do tej metody dostępu 

};

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

class Rectangle : public BaseRectangle { //prostokąt 

	friend std::ostream& operator<<(std::ostream&, const BaseRectangle&);

	virtual std::ostream& operator<<(std::ostream& os) const { //prywatna sekcja by nie moc stosować zapisu w stylu: 
//Rectangle << cout; ( z tej metody korzysta funkcja zaprzyjaźniona )
		os << "width = " << width << ", height = " << height << std::endl;
		os << "pos(x,y) = ( " << returnPos().x << ", " << returnPos().y << " )\n";
		return os;
	}

private:
	int width;
	int height;
public:
	Rectangle(int w=0, int h=0, int x=0, int y=0) : BaseRectangle(x,y), width(w), height(h) {}

	virtual long area() {
		return width*height;
	}
};

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

class Square : public BaseRectangle { //kwadrat

	friend std::ostream& operator<<(std::ostream&, const BaseRectangle&);

	virtual std::ostream& operator<<(std::ostream& os) const { //taka sama sytuacja
		os << "dimension = " << dimension << std::endl;
		os << "pos(x,y) = ( " << returnPos().x << ", " << returnPos().y << " )\n";
		return os;
	}

private:
	int dimension;
public:

	Square(int d=0, int x=0, int y=0) : BaseRectangle(x, y), dimension(d) {}

	virtual long area() {
		return dimension*dimension;
	}

};

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

std::ostream& operator<<(std::ostream& os, const BaseRectangle& bs) {
	bs << os; //wywołanie prywatnej metody 
//operatora<< w zależności od podanego argumentu
	return os;
}

Błędy kompilacji jaki mam:

Jestem w temacie dziedziczenia jeszcze bardzo zielony, trochę tu nakombinowałem i musiałem coś zawalić, wiecie może co? Czy w ogóle dobrze myślałem/kombinowałem nad sposobem rozwiązania tego problemu?

Z góry bardzo wam dziękuje za pomoc ;)

komentarz zamknięcia: dostałem treściwą odpowiedź

1 odpowiedź

0 głosów
odpowiedź 3 czerwca 2018 przez monika90 Pasjonat (22,940 p.)
wybrane 3 czerwca 2018 przez Jakub 0
 
Najlepsza

Definicje funkcji w plikach nagłówkowych powinny mieć atrybut inline

Radziłabym nazwać funkcję wirtualną jakoś inaczej, bo teraz można się pogubić, np:

virtual void print(std::ostream&) const = 0;

oraz używać override.

Nie ma też potrzeby zaprzyjaźniania operatora << z każdą klasą pochodną.

komentarz 3 czerwca 2018 przez Jakub 0 Pasjonat (23,120 p.)
Dziękuje za pomoc, teraz to wszystko wygląda zgrabniej. Ale że tak się zapytam, bo nadal nie jestem pewny. Co było właściwie przyczyną wcześniejszego błędu kompilacji?
1
komentarz 3 czerwca 2018 przez monika90 Pasjonat (22,940 p.)
Pewnie inkludowałeś plik z definicją operatora << w dwóch plikach cpp, więc miałeś dwie definicje, a to jest niedopuszczalne dla funkcji (i zmiennych), no chyba że są inline, to wtedy można mieć wiele definicji te samej funkcji lub zmiennej, pod warunkiem, że te definicje są identyczne.

Powinieneś też coś zrobić z destruktorem w klasie bazowej, np. zdefiniować jej publiczny wirtualny destruktor.
komentarz 3 czerwca 2018 przez Jakub 0 Pasjonat (23,120 p.)
Faktycznie :/

Z tym destruktorem wirtualnym to wiem że jest ważny ale na śmierć go ciągle zapominam wstawić. Co do funkcji zaprzyjaźnionej niby też wiedziałem że powinna być w pliku .cpp albo być inline. Jednak o tym też zapomniałem ( i nie zauważyłem że dodaję ten plik nagłówkowy do innych plików programu ). A najgorsze jest to że sam na to nie wpadłem... ;(

Podobne pytania

0 głosów
2 odpowiedzi 583 wizyt
0 głosów
1 odpowiedź 1,302 wizyt
pytanie zadane 26 listopada 2016 w C i C++ przez sofnir Gaduła (4,690 p.)
0 głosów
1 odpowiedź 283 wizyt

93,692 zapytań

142,611 odpowiedzi

323,220 komentarzy

63,220 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

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...