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

Dziedziczony singleton dostepny tylko dla klas

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
0 głosów
616 wizyt
pytanie zadane 31 października 2022 w C i C++ przez Krzysztofs1234 Użytkownik (890 p.)

Dzień dobry,

obecnie zajmuje się serią Pana Zelenta o programowaniu obiektowym w C++. Realizowałem zadanie z odc. 3, gdzie trzeba było utworzyć klasę do tworzenia wydarzeń zawierających nazwę, datę, czas itd.

Stworzyłem klasę "Event" i w metodzie void show() chciałem dodatkowo wyświetlić nazwy miesięcy oraz dni tygodnia. Potrzebowałem do tego czegoś, co:

1. jest stałe (w sensie niezmienne),

2. tworzy się co najwyżej raz w całym programie, bo ma służyć tylko do odczytu,

3. i jest dostępne wyłącznie dla wybranych klas (tu jest jedna, ale zakładam możliwość dostępu dla wielu).

 

Po wielu próbach postawiłem na klasę singleton "MonthsAndDays" z chronionymi polami jako mapy. W funkcji main nie mogę skorzystać z żadnej metody ani pola tej klasy, więc myślę, że osiągnąłem punkt 3. Co do punktów 1 i 2 już nie mam pewności, a jedynie przypuszczenia, że tak jest. Kolejną wątpliwością jest to, czy nie dało się tego zrobić lepiej, bo nieraz słyszałem i czytałem, że raczej już się singletona nie używa.

 

Co do punktu 2., czy na pewno jest tak, że w momencie, gdy obiekt klasy "Event" skorzysta z klasy "MonthsAndDays" jako pierwszy, to wtedy tworzy się ta jedyna instancja i od tej chwili każdy inny obiekt z dow. klasy będzie do niej odwoływał?

I czy w ogóle ja tutaj tworzę jakąkolwiek instancję klasy "MonthsAndDay", czy jedynie odwołuje się do jej pól? (blok nr 4, funkcja show)

 

Main:

#include "Event.h"

#include <iostream>
using namespace std;

int main()
{
    Event event;

    event.show();

    cout << endl;
    event.load();

    cout << endl;
    event.show();

    return 0;
}

 

MonthsAndDays.h:

#ifndef MonthsAndDays_H
#define MonthsAndDays_H

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


class MonthsAndDays
{
protected:
    const map<unsigned short, string> month_in_words=
    {
        {1, "stycznia"},
        {2, "lutego"},
        {3, "marca"},
        {4, "kwietnia"},
        {5, "maja"},
        {6, "czerwca"},
        {7, "lipca"},
        {8, "sierpnia"},
        {9, "wrzesnia"},
        {10, "pazdziernika"},
        {11, "listopada"},
        {12, "grudnia"}
    };

    const map<unsigned short, string> weeks_day_in_words=
    {
        {0, "sobota"},
        {1, "niedziela"},
        {2, "poniedzialek"},
        {3, "wtorek"},
        {4, "sroda"},
        {5, "czwartek"},
        {6, "piatek"}
    };


protected:
    MonthsAndDays(){};


protected:
    static MonthsAndDays &getMonthsAndDays()
    {
        static MonthsAndDays instance;
        return instance;
    }

};

#endif // MonthsAndDays_H

 

Event.h:

#ifndef EVENT_H
#define EVENT_H

#include "MonthsAndDays.h"

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

typedef unsigned short ushort;


class Event: private MonthsAndDays
{
private:
    string name;
    string place;
    unsigned short day, month, year;
    unsigned short hour, minutes;

    unsigned short weeks_day;


public:
    Event(string="nazwa", string="miejsce", ushort=1, ushort=1, ushort=2000, ushort=0, ushort=0);
    ~Event();

    void load();
    void show() const;

private:
    void Zellers_congruity();
    unsigned short Zellers_month() const;
    unsigned short Zellers_century() const;
    unsigned short Zellers_century_year() const;

};

#endif // EVENT_H

 

Event.cpp:

#include "Event.h"


Event::Event(string name, string place, unsigned short day, unsigned short month, unsigned short year, unsigned short hour, unsigned short minutes)
{
    this->name=name;
    this->place=place;
    this->day=day;
    this->month=month;
    this->year=year;
    this->hour=hour;
    this->minutes=minutes;
}


Event::~Event()
{
    cout << endl << "Usuwanie obiektu." << endl;
}


void Event::load()
{
    cout << "KREATOR WYDARZEN" << endl << endl;

    cout << "Nazwa: ";
    getline(cin, name);

    cout << "Miejsce: ";
    getline(cin, place);

    cout << "Dzien miesiaca: ";
    cin >> day;

    cout << "Numer miesiaca: ";
    cin >> month;

    cout << "Rok: ";
    cin >> year;

    cout << "Godzina (sama godzina): ";
    cin >> hour;

    cout << "Minuty: ";
    cin >> minutes;

    Zellers_congruity();
 }


void Event::show() const
{
    cout << "Wydarzenie '" << name << "' odbedzie sie w miejscu " << place << " " << day << " " <<  MonthsAndDays::month_in_words.at(month) << " ";
    cout << year << " roku" << " (" << MonthsAndDays::weeks_day_in_words.at(weeks_day) << ") " << "o godzinie " << hour << ":" << minutes << "." << endl;
}

void Event::Zellers_congruity()
{
    unsigned short J=Zellers_century(), K=Zellers_century_year();
    weeks_day=(day + 13*(Zellers_month()+1)/5 + J/4 + 5*J + K + K/4)%7;
}


unsigned short Event::Zellers_month() const
{
    if(month<3) return month+12;
    return month;
}


unsigned short Event::Zellers_century() const
{
    return year/100;
}


unsigned short Event::Zellers_century_year() const
{
    return year%100;
}

 

1 odpowiedź

+1 głos
odpowiedź 31 października 2022 przez j23 Mędrzec (195,260 p.)

czy na pewno jest tak, że w momencie, gdy obiekt klasy "Event" skorzysta z klasy "MonthsAndDays" jako pierwszy, to wtedy tworzy się ta jedyna instancja i od tej chwili każdy inny obiekt z dow. klasy będzie do niej odwoływał?

Jeśli mowa o instancji stworzonej w getMonthsAndDays - tak.

I czy w ogóle ja tutaj tworzę jakąkolwiek instancję klasy "MonthsAndDay", czy jedynie odwołuje się do jej pól?

W przypadku Event::show odwołujesz się do pól, które klasa Event odziedziczyła (po co?). Jak chcesz by klasa Event miała dostęp do pól MonthsAndDays, zaprzyjaźnij ją.

singleton "MonthsAndDays" z chronionymi polami jako mapy.

Te mapy to overkill. Zwykłe tablice wystarczą.

komentarz 31 października 2022 przez Krzysztofs1234 Użytkownik (890 p.)
Dziękuję za rozwianie części wątpliwości. Faktycznie, te tablice zamiast map... Nie wpadło mi do głowy, a wystarczyłyby dwie statyczne tablice string.

Następnie, jeśli odwołuję się do pól, to wnioskuję, że nie muszę tworzyć żadnej instancji, więc wzorzec Singleton jest zbędny. Wystarczyłoby utworzyć tę klasę "MonthsAndDays" i w niej zadeklarować przyjaźń z klasą "Event", która to będzie korzystać z jej pól. I tu ew. mógłbym dać zabezpieczenie przed nieumyślnym stwarzaniem obiektów klasy "MonthsAndDays", choć nie wiem, jak.
komentarz 1 listopada 2022 przez j23 Mędrzec (195,260 p.)

Zaraz, zaraz. Wzorzec singleton oznacza, że masz tylko jedną instancje klasy, do której inne obiekty się odwołują. Coś jak zmienna globalna. Odwoływanie się do pól klasy bazowej (w tym przypadku MonthsAndDays) jest zaprzeczeniem tego wzorca, bo każda instancja klasy Event ma swoją wersję (odziedziczonych pól) klasy MonthsAndDays.

IMO tutaj nie trzeba ani singletona, ani dziedziczenia. Wystarczy to:

struct MonthsAndDays {
    static constexpr char const* monthToText[] =
    {
        "",
        "stycznia",
        "lutego",
        "marca",
        "kwietnia",
        "maja",
        "czerwca",
        "lipca",
        "sierpnia",
        "wrzesnia",
        "pazdziernika",
        "listopada",
        "grudnia"
    };
 
    static constexpr char const* dayOfWeekToText[] =
    {
        "sobota",
        "niedziela",
        "poniedzialek",
        "wtorek",
        "sroda",
        "czwartek",
        "piatek"
    };
};

I wtedy wystarczy odwoływać się przez MonthsAndDays::dayOfWeekToText i MonthsAndDays::monthToText.

komentarz 1 listopada 2022 przez Krzysztofs1234 Użytkownik (890 p.)
edycja 1 listopada 2022 przez Krzysztofs1234

każda instancja klasy Event ma swoją wersję (odziedziczonych pól) klasy MonthsAndDays

Aha, czyli mimo, że nie korzystam z konstruktora ani razu, to i tak każda instancja klasy :"Event" ma swoje pole odziedziczone po "MonthsAndDays"?

Jeśli tak, to niedobrze. Jak wiadomo, moim celem było stworzenie zbioru (powiedzmy zbioru, bo jeszcze nie wiem, jaki to będzie typ danych) danych, które służą tylko do odczytu, są niezmienne w swojej postaci i (co wynika z pierwszego założenia) nie mogą być powielane.

Skoro przez dziedziczenie, mimo że nie korzystam z konstruktora klasy dziedziczonej, każda instancja klasy potomnej i tak ma osobne pola klasy, po której dziedziczy, no to dziedziczenie w ogóle odpada.

Klasy zaprzyjaźnione też odpadają, bo podejrzewam, że będzie sytuacja jw., czyli każda instancja klasy "Event" będzie miała własne pola klasy zaprzyjaźnionej "M&D".

Zaproponowana przez Ciebie struktura jest o tyle dla mnie ciekawa, że nie znałem słowa kluczowego "constexpr". Chyba nie do końca rozumiem, co zostało stworzone, choć wstępnie pojmuję to tak: struktura (dwóch) statycznych stałych wyrażeń będących stałymi wskaźnikami do tablic tablic (tak, tablic tablic) znaków. Obawiam się też, że ta struktura może nie spełnić wymagania ograniczonego dostępu, tj. tylko dla klasy "Event" i ew. innych klas.

 

komentarz 1 listopada 2022 przez j23 Mędrzec (195,260 p.)

Aha, czyli mimo, że nie korzystam z konstruktora ani razu, to i tak każda instancja klasy :"Event" ma swoje pole odziedziczone po "MonthsAndDays"?

Tak. Co do konstruktora, to on jest wywoływany niejawnie w konstruktorze klasy potomnej.

(...) każda instancja klasy potomnej i tak ma osobne pola klasy, po której dziedziczy, no to dziedziczenie w ogóle odpada.

Można zrobić tak, że pola klasy MonthsAndDays są statyczne, wtedy będzie tylko jedna instancja tych pól dla wszystkich instancji klas pochodnych. Rzecz w tym, że dziedziczenie powinno mieć jakiś logiczny sens. IMO dziedziczenie MonthsAndDays przez klasę Event jest trochę jakby klasa Samochód dziedziczyła po klasie Słoń. Bez sensu.

Klasy zaprzyjaźnione też odpadają, bo podejrzewam, że będzie sytuacja jw., czyli każda instancja klasy "Event" będzie miała własne pola klasy zaprzyjaźnionej "M&D".

Nie wiem, skąd ten wniosek? Jeśli to singleton, to będzie tylko jedna instancja klasy. Zaprzyjaźnianie jedynie "poluźnia" reguły dostępu dla danej klasy, i tyle.

Obawiam się też, że ta struktura może nie spełnić wymagania ograniczonego dostępu, tj. tylko dla klasy "Event" i ew. innych klas.

No to zmień struct na class i daj zaprzyjaźnienie z klasą Event.

constexpr dałem, by móc przypisać wartości tablicom wewnątrz definicji klasy. Inaczej musiałbym przypisanie dać poza klasą, co zaciemniłoby nieco kod.

pojmuję to tak: struktura (dwóch) statycznych stałych wyrażeń będących stałymi wskaźnikami do tablic

To nie są wskaźniki do tablic, tylko odwrotnie - tablice wskaźników (do literałów tekstowych) ;)

komentarz 1 listopada 2022 przez Krzysztofs1234 Użytkownik (890 p.)
edycja 1 listopada 2022 przez Krzysztofs1234

Dziękuję za liczne odpowiedzi. 

Nie wiem, skąd ten wniosek? Jeśli to singleton, to będzie tylko jedna instancja klasy. Zaprzyjaźnianie jedynie "poluźnia" reguły dostępu dla danej klasy, i tyle.

Wniosek pochodzi z:

każda instancja klasy Event ma swoją wersję (odziedziczonych pól) klasy MonthsAndDays.

Skoro każda instancja klasy "Event" ma własne pola z dziedziczonej klasy, stwierdziłem, że tak samo każda instancja klasy "Event" wywołując MonthsAndDays::<jedno z dwóch pól> (bo chodzi odczytanie pola) też będzie korzystać ze swojej wersji pól. Stwierdzenie, po tym, co mi napisałeś, uznaję za błędne. Przyznam, że implikacja tutaj nie była do końca trafiona, bo sytuacje - dziedziczenie i klasy zaprzyjaźnione - nie są analogiczne.

Będąc jeszcze w temacie piszesz, że:

Co do konstruktora, to on jest wywoływany niejawnie w konstruktorze klasy potomnej.

Jeśli instancji klasy "M&D" będzie tyle, co obiektów "Event", to co robi ten singleton, skoro nie zapobiega tworzeniu więcej niż jednej? 

Chyba że to działa tak, iż klasa "Event" w każdym swym obiekcie dziedziczy ciało klasy "M&D", w którym to są te wszystkie pola i metody wraz z konstruktorem i wzorcem singleton. Wtedy też dla każdego obiektu klasy "Event" może powstać co najwyżej jedna instancja klasy "M&D". Czy tak?

 

EDIT:

No i wracając do tych klas zaprzyjaźnionych, jeśli w klasie "M&D" zadeklaruję przyjaźń z klasą "Event" i w wielu obiektach klasy "Event" użyję MonthsAndDays::<jedno z dwóch pó> to jedynie wskazuję na pola klasy i nie tworzę żadnych instancji?

komentarz 2 listopada 2022 przez j23 Mędrzec (195,260 p.)

Jeśli instancji klasy "M&D" będzie tyle, co obiektów "Event", to co robi ten singleton, skoro nie zapobiega tworzeniu więcej niż jednej? 

Nie wiem, co robi. Taki dałeś kod i do niego miałem swoje uwagi. Gdybyś konstruktor klasy MonthsAndDays zrobił prywatnym, a getMonthsAndDays publicznym, wtedy miałbyś singleton i nie mógłbyś dziedziczyć tej klasy. Byłaby tylko jedna instancja tej klasy.

jeśli w klasie "M&D" zadeklaruję przyjaźń z klasą "Event" i w wielu obiektach klasy "Event" użyję MonthsAndDays::<jedno z dwóch pó> to jedynie wskazuję na pola klasy i nie tworzę żadnych instancji?

Tak. Te dwie tablice są statyczne (w sensie static), one istnieją niezależnie od obiektów klasy MonthsAndDays.

komentarz 3 listopada 2022 przez Krzysztofs1234 Użytkownik (890 p.)

Nie wiem, co robi. Taki dałeś kod i do niego miałem swoje uwagi.

 Chodziło o to, że mimo iż użyłem singletona, nie miałem pożądanego działania - zapewnionej maksymalnie jednej instancji. Co się teraz mi rozjaśniło - z klasy ze wzorcem singleton nie można dziedziczyć, bo to wymaga rezygnacji z prywatności konstruktora, więc odkąd zacząłem dziedziczyć już to nie był singleton.

Dzięki za rozmowę. Nie dość, że przeczytałem o rozwiązaniu, które spełni moje wymagania, to jeszcze dowiedziałem się paru ciekawych rzeczy, co przyczyni się do lepszego rozumienia klas.

Podobne pytania

0 głosów
2 odpowiedzi 256 wizyt
pytanie zadane 29 października 2016 w C i C++ przez easytodo Mądrala (5,380 p.)
0 głosów
4 odpowiedzi 830 wizyt
pytanie zadane 10 kwietnia 2017 w C i C++ przez niezalogowany
0 głosów
1 odpowiedź 477 wizyt
pytanie zadane 11 grudnia 2016 w C i C++ przez DragonCoder Nałogowiec (36,500 p.)

93,164 zapytań

142,176 odpowiedzi

321,938 komentarzy

62,493 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 688p. - dia-Chann
  2. 676p. - CC PL
  3. 675p. - Łukasz Piwowar
  4. 664p. - Marcin Putra
  5. 662p. - Łukasz Eckert
  6. 649p. - Michal Drewniak
  7. 641p. - rucin93
  8. 641p. - rafalszastok
  9. 629p. - Piotr Aleksandrowicz
  10. 629p. - Adrian Wieprzkowicz
  11. 621p. - Dawid128
  12. 611p. - ksalekk
  13. 606p. - Mariusz Fornal
  14. 602p. - Michał Telesz
  15. 597p. - Hubert Chęciński
Szczegóły i pełne wyniki

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

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...