Nie wiem jakie masz ograniczenia co do rozwiązania samego zadania. Szczególnie jeśli zostało Ci zlecone. Więc traktuj uwagi jako uwagi do kodu który pojawiał by się w ew. projekcie.
- Dziedziczenie Punkt -> Okrąg jest średnim pomysłem. Ci wspólnego ma pojęcie punktu z pojęciem okręgu? Raczej jest tak że obiekt okrąg, posiada czyli agreguje lub wkomponowuje punkt. Agreguje jeśli nie będzie odpowiedzialny za jego zniszczenie a wkomponowuje jeśli w trakcie destrukcji obiektu typu Okrąg, zniszczy także obiekt typu Punkt. W konsekwencji, nie ma sensu tworzyć obiektu Okrąg bez... środka! Można więc mu ten środek podać w konstruktorze.
- Aby uniknąć niechcianych konwersji Okrag <- Punkt, konstruktor z argumentem środka, należy uczynić explicit.
- Jeśli można, warto unikać relacji friend. To dehermetyzacja obiektu z utratą kontroli kto z czego korzysta. Lepszym rozwiązaniem jest tworzenie dedykowanej metody dostępu do danych. W rozwiązaniu z friend, wystarczy że nazwę funkcję tak jak w deklaracji przyjaźni i już dostaję się do wszystkich elementów prywatnych klasy. W rozwiązaniu z dedykowaną funkcją. masz pełną kontrolę.
- Można rozważyć tworzenie punktu bez podawania współrzędnych. Wtedy będzie posiadał współrzędne 0, 0.
- Należy preferować inicjalizację atrybutów w ramach listy inicjalizacyjnej. Unikniesz w ten sposób dwukrotnego tworzeniu obiektu. Jeden raz przez domyślny konstruktor przed wejściem do ciała konstruktora, drugi raz w trakcie inicjowania w jego ciele.
- Funkcje które nie dokonują zmian w obiekcie, powinny mieć atrybut const. Tak aby chronić przed pomyłkowymi zmianami na przyszłość.
- Jeśli przyjmujesz konwencję nazywania klasy w stylu CamelCase, nazwy atrybutów i metod rozpoczynają się wtedy od małej litery. Wiem że to raczej konwencja, ale warto się jej trzymać.
- Nie używaj using namespace std; , Używaj pełnego dostępu kwalifikowanego.
Oczywiście brakuje jeszcze innych metod w Okrag i Punkt. Tu jednak nie wiem jakie mają być zastosowania.
Najważniejszym argumentem za tym aby nie stosować (bez potrzeby) rzutowania na wskaźnik w ramach drzewa dziedziczenia, jest łamanie reguły Zasady Podstawienia Liskov https://pl.wikipedia.org/wiki/Zasada_podstawienia_Liskov . Rzutowanie jest objawem łamania tej reguły.
Oczywiście "jeśli Ci kazali", nie mam pytań. Wiedz jednak że dobra praktyka jest bliżej tego co opisałem.
Ja bym zrobił to tak:
#include <iostream>
class Punkt{
public:
Punkt(int x_ = 0, int y_ = 0): x(x_), y(y_) {
}
~Punkt(){
}
std::ostream& reprezentacja(std::ostream &output) const {
output << "(" << x << "," << y << ")";
return output;
}
private:
int x;
int y;
};
std::ostream &operator<<(std::ostream &output, const Punkt &d) {
return d.reprezentacja(output);
}
class Okrag {
public:
explicit Okrag(const Punkt& p_): srodek(p_), promien(1) {
}
std::ostream& reprezentacja(std::ostream &output) const {
output << "Okrag: [Promien: " << promien << ", Srodek";
srodek.reprezentacja(output);
output << ']';
return output;
}
private:
int promien;
Punkt srodek;
};
std::ostream &operator<<(std::ostream &output, const Okrag &d) {
d.reprezentacja(output);
return output;
}
int main() {
Punkt p(10, 20);
Okrag o(p);
std::cout << o << std::endl;
}