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

Konstruktory w c++ (w dziedziczeniu)

Object Storage Arubacloud
+1 głos
237 wizyt
pytanie zadane 1 marca 2023 w C i C++ przez Pioterer Początkujący (280 p.)

Mam za zadanie napisać program pozwalający przetwarzać dane samochodów w c++ jako przygotowanie do sprawdzianu i natknąłem się na problem który nie wiem jak rozwiązać. Mam sprawić aby dane były wprowadzane przez użytkownika i niby to osiągnąłem, ale nie tak jak chciałem. Pragnę by ten kod można by później zmienić i używać hermetyzacji (co za tym idzie setterów i getterów) i nie za bardzo wiem jak to zrobić bo w moim kodzie jakby używane są nowe zmienne w mainie jak źle wytłumaczyłem to proszę pisać, spróbuję w inny sposób wytłumaczyć. Chciałbym to zrobić bez wprowadzania nowych zmiennych w mainie.

#include <iostream>
using namespace std;

//Bazowy Pojazd
class Pojazd {
public:
    string marka, model;
    int rok_pr;

    Pojazd () {}
    Pojazd (string pMarka, string pModel, int pRok_pr) {
        marka = pMarka;
        model = pModel;
        rok_pr = pRok_pr;
    }

    void wyswietl () {
        cout << endl << "Dane:\n Marka: " << marka << "\n Model: " << model << "\n Rok produkcji: " << rok_pr;
    }
};

//Autobus
class Autobus : public Pojazd {
public:
    int miej_siedz, miej_stoj;

    Autobus () {}
    Autobus (string pMarka, string pModel, int pRok_pr, int pMiej_siedz, int pMiej_stoj) : Pojazd (pMarka, pModel, pRok_pr) {
        miej_siedz = pMiej_siedz;
        miej_stoj = pMiej_stoj;
    }

    void wyswietl () {
        cout << endl << "Dane:\n Marka: " << marka << "\n Model: " << model << "\n Rok produkcji: " << rok_pr << "\n Miejsca siedzace: " << miej_siedz << "\n Miejsca stojace: " << miej_stoj;
    }
};

//Ciezarowka
class Ciezarowka : public Pojazd {
public:
    string cysterna, wywrotka, chlodnia;

    Ciezarowka () {}
    Ciezarowka (string pMarka, string pModel, int pRok_pr, string pCysterna, string pChlodnia, string pWywrotka) : Pojazd (pMarka, pModel, pRok_pr) {
        pCysterna = cysterna;
        pChlodnia = chlodnia;
        pWywrotka = wywrotka;
    }

    void wyswietl() {
        cout << endl << "Dane:\n Marka: " << marka << "\n Model: " << model << "\n Rok produkcji: " << rok_pr << "\n Cysterna: " << cysterna << "\n Chlodnia: " << chlodnia << "\n Wywrotka: " << wywrotka;
    }
};

//Main
int main () {
    string sMarka, sModel, sCysterna, sChlodnia, sWywrotka;
    int sRok_pr, sMiej_siedz, sMiej_stoj;

    cout << "Wprowadz Marke: ";
    cin >> sMarka;
    cout << "Wprowadz model: ";
    cin >> sModel;
    cout << "Wprowadz rok produkcji: ";
    cin >> sRok_pr;
    cout << "Wprowadz miejsca siedzace: ";
    cin >> sMiej_siedz;
    cout << "Wprowadz miejsca stojace: ";
    cin >> sMiej_stoj;

    Autobus bus(sMarka, sModel, sRok_pr, sMiej_siedz, sMiej_stoj);

    bus.wyswietl();

    return 0;
}

 

1
komentarz 2 marca 2023 przez tkz Nałogowiec (42,000 p.)
A co autobus ma wspólnego z wczytywaniem danych? Imo sposób OPa jest lepszy. Lepsze rozdzielenie samej klasy od inputu, który jest niezależny mimo wszystko.

Dodając gettery i setery właściwie nie tworzysz żadnej hermetyzacji. Ba! Nawet bym się pokusił, że takie rozwiązania znacznie częściej rozhermetyzowują klasy. Bo czym się różni publiczne pole od setera i gettera do tego właśnie pola?
komentarz 2 marca 2023 przez Oscar Nałogowiec (29,290 p.)
Jeśli setter i getter byłyby trywialne to faktycznie wszystko jedno, ale zawsze jest kontrola w momencie modyfikacji, taki swego rodzaju trigger, gdzie można zwalidatować dane, dodatkowo coś zmodyfikować itp. Jednak w takim przypadku jak w zadaniu zastanawiałbym się czy te pola nie powinny być wręcz const - przecież np. marka samochodu, model, rok produkcji itp się nie zmieni.
1
komentarz 2 marca 2023 przez reaktywny Nałogowiec (40,990 p.)
W autobusie musisz sprawdzić czy wszyscy wykupili bilety na przejazd !
1
komentarz 2 marca 2023 przez tkz Nałogowiec (42,000 p.)

@Oscar, Jeżeli potrzebujesz nietrywialnego gettera lub setera, to powinno paść pytanie czy nie łamiesz zasady single responsibility.  

@reaktywny, i to jest idealny przykład! Teraz potrzebowalibyśmy odpowiedzialności dla biletów, bo co obchodzi pojazd czy ktoś ma jakiś papierek, czy też nie.

komentarz 2 marca 2023 przez reaktywny Nałogowiec (40,990 p.)

To był oczywiście dżołk z mojej strony, ale kto wie czy Pioterer nie rozbuduje swojej appki znacznie bardziej i nie będzie musiał się zmierzyć z tym problemem.

komentarz 2 marca 2023 przez TOWaD Mądrala (5,700 p.)
edycja 2 marca 2023 przez TOWaD

@tkz,

  Bo czym się różni publiczne pole od setera i gettera do tego właśnie pola?

pl ,en. Bo raczej nie jest to dobra praktyka, choć nie wiem dlaczego Wydaje mi się ,że jest różnica (ale to tylko takie przeczucie).

1
komentarz 2 marca 2023 przez tkz Nałogowiec (42,000 p.)

@reaktywny, wyłapane, ale idealnie mi pasowało xD
 

@TOWaD, z punktu widzenia hermetyzacji nie ma to kompletnie znaczenia. W obu przypadkach masz dostęp, zarówno do zapisu i odczytu. Oczywiście, o ile chcesz mieć tylko odczyt, tak jak wspomniał @Oscar są pola, które powinny być const, co za tym idzie setter jest kompletnie bez sensu. (Zostawienie pola const jako publiczne również się sprowadzi do tego, że będzie właściwie read-only, ale to już inna kwestia.)
Odnosząc się do linku. Jak widzę zwrot nigdy/zawsze, to coś mnie w środku skręca. Coś jak z singletonem - nigdy nie używać *pochodnie płoną, a widły uderzają o beton*. Ale! W linku jest poruszany temat specyficznego pola jakim jest id, które nomen-omen jest nie zmienne. Więc tak jak wspominałem wcześniej apropos const'a, getter będzie spoko, bo nie będzie setera. 

komentarz 3 marca 2023 przez TOWaD Mądrala (5,700 p.)

Jak widzę zwrot nigdy/zawsze,

Masz  plusa za to, w tym kontekście. 

Ale zupełnie nie o ty mi chodziło. Ale jest tak zwany, zbiór dobrych praktyk, i jak się człowiek nie zna/(jest początkujący) powinien raczej go stosować, by nie mieć błędów ciężkich do wykrycia.

Jak jest się tuzem to możesz robić to co chcesz, bo wiesz dlaczego. Aczkolwiek nie wiem czy jak coś jest bardzo skomplikowanie to, to jest zasada jak działa to nie ruszać.

Np zastanawiałem się dlaczego C::B dodaje virtulany destruktor jak dodaje klasę. Później  oglądałem gdzieś na internecie i ktoś poruszał tą kwestię i to miało znaczenie.

Tak samo z tymi polami jak się wie, to róbta co chceta.

1 odpowiedź

+2 głosów
odpowiedź 2 marca 2023 przez mokrowski Mędrzec (155,460 p.)
edycja 2 marca 2023 przez mokrowski

Warto zrobić na początek porządek z tym kodem:

Jeśli chcesz aby kod był ponownie używany, lepiej jednak nie deklarować na początku użycia przestrzeni nazewniczej std. Tu można dyskutować w jakim przypadku i przy jakiej objętości kodu...

Brakuje Ci nagłówka string.

Dobrą zasadą jest deklarowanie zmiennych w nowej linii. To się łatwiej czyta i nie popełni się błędu jeśli typ będzie skomplikowany (np. wskaźnik).

Dobrą zasadą jest przyjęcie łamania linii na określonej kolumnie. Tu złamię (co jest najczęściej wykładnią) na 80.

Inicjalizacja atrybutów klasy powinna odbywać się w liście inicjalizacji.

Jeśli hermetyzujesz, umieść atrybuty w sekcjach private.

Stosu const correctness tam gdzie to możliwe.

Po co kopiować std::string w argumencie? Wystarczy przekazać referencję na stałą.

Na początek, aby mocno nie zmieniać kodu, dostęp do atrybutów w klasie Pojazd można deklarować jako protected (później można to ew zmienić).

Znaków nowej linii raczej nie wprowadza się na początku tekstu a w częściej na końcu. I nie jest to std::endl. Wystarcza znak nowej linii (to kiedy konieczne jest std::endl które realizuje std::flush, to poza tym przykładem)

Powstaje sensowne pytanie czy zajdzie przypadek tworzenia "pustego autobusu". To co robisz konstruktorem domyślnym (czyli bez argumentów). Jeśli nie zajdzie taki przypadek, warto poinformować kompilator że nawet gdy go przypadkowo użyjesz, to będzie błąd.

W zasadzie nie zmieniłem działania Twojego kodu. Wygląda tak:

#include <iostream>
#include <string>

//Bazowy Pojazd
class Pojazd {
public:
    Pojazd () = delete;
    Pojazd (const std::string & pMarka, const std::string & pModel, int pRok_pr) 
    	: marka{pMarka}, model{pModel}, rok_pr{pRok_pr} {}
 
    void wyswietl () const {
	    std::cout << "Dane:\n Marka: " << marka << "\n Model: " << model
	    	    << "\n Rok produkcji: " << rok_pr << '\n';
    }
protected:
    std::string marka;
    std::string model;
    int rok_pr;
};
 
//Autobus
class Autobus : public Pojazd {
public:
    Autobus () = delete;
    Autobus (const std::string & pMarka, const std::string & pModel, int pRok_pr,
    		    int pMiej_siedz, int pMiej_stoj)
    	    : Pojazd (pMarka, pModel, pRok_pr), miej_siedz{pMiej_siedz}
    		, miej_stoj{pMiej_stoj} {}
 
    void wyswietl () const {
	    std::cout << "Dane:\n Marka: " << marka << "\n Model: " << model
		<< "\n Rok produkcji: " << rok_pr << "\n Miejsca siedzace: "
		<< miej_siedz << "\n Miejsca stojace: " << miej_stoj << '\n';
    }
private:
    int miej_siedz;
    int miej_stoj;
};
 
//Ciezarowka
class Ciezarowka : public Pojazd {
public:
    Ciezarowka () = delete;
    Ciezarowka (const std::string & pMarka, const std::string & pModel,
    		    int pRok_pr, const std::string & pCysterna,
    		    const std::string & pChlodnia,
    		    const std::string & pWywrotka)
    	    : Pojazd (pMarka, pModel, pRok_pr), cysterna{pCysterna},
    	    	chlodnia{pChlodnia}, wywrotka{pWywrotka} {}
 
    void wyswietl() const {
	    std::cout << "Dane:\n Marka: " << marka << "\n Model: " << model
	    	    << "\n Rok produkcji: " << rok_pr << "\n Cysterna: "
	    	    << cysterna << "\n Chlodnia: " << chlodnia
	    	    << "\n Wywrotka: " << wywrotka << '\n';
    }
private:
    std::string cysterna;
    std::string wywrotka;
    std::string chlodnia;
};
 
//Main
int main () {
    std::string sMarka;
    std::string sModel;
    std::string sCysterna;
    std::string sChlodnia;
    std::string sWywrotka;

    int sRok_pr;
    int sMiej_siedz;
    int sMiej_stoj;
 
    std::cout << "Wprowadz Marke: ";
    std::cin >> sMarka;
    std::cout << "Wprowadz model: ";
    std::cin >> sModel;
    std::cout << "Wprowadz rok produkcji: ";
    std::cin >> sRok_pr;
    std::cout << "Wprowadz miejsca siedzace: ";
    std::cin >> sMiej_siedz;
    std::cout << "Wprowadz miejsca stojace: ";
    std::cin >> sMiej_stoj;
 
    Autobus bus(sMarka, sModel, sRok_pr, sMiej_siedz, sMiej_stoj);
 
    bus.wyswietl();
 
    return 0;
}

Teraz powstaną pytania:

  1. Czy markę i model należy zmieniać w trakcie działania aplikacji? Czy tak łatwo ten sam egzemplarz pojazdu, zmienić na inną markę?
  2. Na ile klasa Pojazd powinna udostępniać możliwość dostępu i ew. zmiany atrybutów?

Na pierwsze pytanie można odpowiedzieć że nie! Nie zmienisz Opla w Mercedesa ... w tym samym egzemplarzu. Toteż pola marka i model powinny być stałe. A co z rokiem? Hmm... nie chodzi o aplikację dla oszustów, ale rzeczywiście po zmianie ramy, karoserii i silnika, może się zmienić rocznik. Więc warto dać dostęp do zmiany rocznika.

Na pytanie drugie... Jeśli powinna dawać możliwość zmiany i dostępu, to tylko dla swoich potomków. Stąd nie będzie dostępu publicznego do pól. I to tak w trybie odczytu jak i zapisu. Widoczność metod dostępowych będzie protected a same atrybuty prywatne.

Dla porządku o czym mówię, kod:

#include <iostream>
#include <string>

//Bazowy Pojazd
class Pojazd {
public:
    Pojazd () = delete;
    Pojazd (const std::string & pMarka, const std::string & pModel, int pRok_pr) 
    	: marka{pMarka}, model{pModel}, rok_pr{pRok_pr} {}
 
    void wyswietl () const {
	    std::cout << "Dane:\n Marka: " << marka << "\n Model: " << model
	    	    << "\n Rok produkcji: " << rok_pr << '\n';
    }

protected:
    const std::string & get_marka() const {
    	    return marka;
    }
    const std::string & get_model() const {
    	    return model;
    }
    const int get_rok_pr() const {
    	    return rok_pr;
    }

    void set_rok_pr(int rok) {
    	    rok_pr = rok;
    }

private:
    const std::string marka;
    const std::string model;
    int rok_pr;
};
 
//Autobus
class Autobus : public Pojazd {
public:
    Autobus () = delete;
    Autobus (const std::string & pMarka, const std::string & pModel, int pRok_pr,
    		    int pMiej_siedz, int pMiej_stoj)
    	    : Pojazd (pMarka, pModel, pRok_pr), miej_siedz{pMiej_siedz}
    		, miej_stoj{pMiej_stoj} {}
 
    void wyswietl () const {
	    std::cout << "Dane:\n Marka: " << get_marka() << "\n Model: "
	    	<< get_model() << "\n Rok produkcji: " << get_rok_pr()
	    	<< "\n Miejsca siedzace: " << miej_siedz
	    	<< "\n Miejsca stojace: " << miej_stoj << '\n';
    }
private:
    int miej_siedz;
    int miej_stoj;
};
 
//Ciezarowka
class Ciezarowka : public Pojazd {
public:
    Ciezarowka () = delete;
    Ciezarowka (const std::string & pMarka, const std::string & pModel,
    		    int pRok_pr, const std::string & pCysterna,
    		    const std::string & pChlodnia,
    		    const std::string & pWywrotka)
    	    : Pojazd (pMarka, pModel, pRok_pr), cysterna{pCysterna},
    	    	chlodnia{pChlodnia}, wywrotka{pWywrotka} {}
 
    void wyswietl() const {
	    std::cout << "Dane:\n Marka: " << get_marka() << "\n Model: "
	    	    << get_model() << "\n Rok produkcji: " << get_rok_pr()
	    	    << "\n Cysterna: " << cysterna << "\n Chlodnia: "
	    	    << chlodnia << "\n Wywrotka: " << wywrotka << '\n';
    }
private:
    std::string cysterna;
    std::string wywrotka;
    std::string chlodnia;
};
 
//Main
int main () {
    std::string sMarka;
    std::string sModel;
    std::string sCysterna;
    std::string sChlodnia;
    std::string sWywrotka;

    int sRok_pr;
    int sMiej_siedz;
    int sMiej_stoj;
 
    std::cout << "Wprowadz Marke: ";
    std::cin >> sMarka;
    std::cout << "Wprowadz model: ";
    std::cin >> sModel;
    std::cout << "Wprowadz rok produkcji: ";
    std::cin >> sRok_pr;
    std::cout << "Wprowadz miejsca siedzace: ";
    std::cin >> sMiej_siedz;
    std::cout << "Wprowadz miejsca stojace: ";
    std::cin >> sMiej_stoj;
 
    Autobus bus(sMarka, sModel, sRok_pr, sMiej_siedz, sMiej_stoj);
 
    bus.wyswietl();
 
    return 0;
}

No to teraz Autobus.

Załóżmy że można w autobusie zmienić ilość miejsc siedzących i stojących. Tu można pobawić się w implementację maksymalnej ilości miejsc siedzących i stojących. Tak aby nie można było "demolować autobusu" :) Potrzebne będą get/set dla tych atrybutów. Tu mogą być publiczne.

Tu akurat zmiany są proste. Kod więc dalej...

Co jednak z Ciężarówką? W Twojej implementacji, może ona być równocześnie i wywrotką i chłodnią i cysterną. Albo rozdziel je na oddzielne klasy (jeśli każdy z tych pojazdów wymaga innej implementacji), albo dodaj opcję przekazywania innej przestrzeni załadunkowej. Ta ostatnia, to może być oddzielna klasa. Tu uproszczę przekazując std::string z opisem.

Znów... dostęp do pola "rodzaj", powinien być raczej tylko do odczytu (chłodnię trudno przerobić na wywrotkę). Stąd jedynie getter.

No to kod:

#include <iostream>
#include <string>

//Bazowy Pojazd
class Pojazd {
public:
    Pojazd () = delete;
    Pojazd (const std::string & pMarka, const std::string & pModel, int pRok_pr) 
    	: marka{pMarka}, model{pModel}, rok_pr{pRok_pr} {}
 
    void wyswietl () const {
	    std::cout << "Dane:\n Marka: " << marka << "\n Model: " << model
	    	    << "\n Rok produkcji: " << rok_pr << '\n';
    }

protected:
    const std::string & get_marka() const {
    	    return marka;
    }
    const std::string & get_model() const {
    	    return model;
    }
    const int get_rok_pr() const {
    	    return rok_pr;
    }

    void set_rok_pr(int rok) {
    	    rok_pr = rok;
    }
private:
    const std::string marka;
    const std::string model;
    int rok_pr;
};
 
//Autobus
class Autobus : public Pojazd {
public:
    Autobus () = delete;
    Autobus (const std::string & pMarka, const std::string & pModel, int pRok_pr,
    		    int pMiej_siedz, int pMiej_stoj)
    	    : Pojazd (pMarka, pModel, pRok_pr), miej_siedz{pMiej_siedz}
    		, miej_stoj{pMiej_stoj} {}
 
    void wyswietl () const {
	    std::cout << "Dane:\n Marka: " << get_marka() << "\n Model: "
	    	<< get_model() << "\n Rok produkcji: " << get_rok_pr()
	    	<< "\n Miejsca siedzace: " << miej_siedz
	    	<< "\n Miejsca stojace: " << miej_stoj << '\n';
    }
    const int get_miej_siedz() const {
    	    return miej_siedz;
    }
    const int get_miej_stoj() const {
    	    return miej_stoj;
    }

    void set_miej_siedz(int miejsca) {
    	    miej_siedz = miejsca;
    }
    void set_miej_stoj(int miejsca) {
    	    miej_stoj = miejsca;
    }
private:
    int miej_siedz;
    int miej_stoj;
};
 
//Ciezarowka
class Ciezarowka : public Pojazd {
public:
    Ciezarowka () = delete;
    Ciezarowka (const std::string & pMarka, const std::string & pModel,
    		    int pRok_pr, const std::string & pRodzaj)
    	    : Pojazd (pMarka, pModel, pRok_pr), rodzaj{pRodzaj} {}
 
    void wyswietl() const {
	    std::cout << "Dane:\n Marka: " << get_marka() << "\n Model: "
	    	    << get_model() << "\n Rok produkcji: " << get_rok_pr()
	    	    << "\n Rodzaj: " << rodzaj << '\n';
    }

    const std::string & get_rodzaj() const {
    	    return rodzaj;
    }
private:
    const std::string rodzaj;
};
 
//Main
int main () {
    std::string sMarka;
    std::string sModel;
    std::string sCysterna;
    std::string sChlodnia;
    std::string sWywrotka;

    int sRok_pr;
    int sMiej_siedz;
    int sMiej_stoj;
 
    std::cout << "Wprowadz Marke: ";
    std::cin >> sMarka;
    std::cout << "Wprowadz model: ";
    std::cin >> sModel;
    std::cout << "Wprowadz rok produkcji: ";
    std::cin >> sRok_pr;
    std::cout << "Wprowadz miejsca siedzace: ";
    std::cin >> sMiej_siedz;
    std::cout << "Wprowadz miejsca stojace: ";
    std::cin >> sMiej_stoj;
 
    Autobus bus(sMarka, sModel, sRok_pr, sMiej_siedz, sMiej_stoj);
 
    bus.wyswietl();
 
    return 0;
}

 

Podobne pytania

–2 głosów
1 odpowiedź 284 wizyt
pytanie zadane 24 października 2022 w C i C++ przez benny13 Obywatel (1,150 p.)
0 głosów
1 odpowiedź 454 wizyt

92,568 zapytań

141,422 odpowiedzi

319,638 komentarzy

61,957 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.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...