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

Statyczny enum dla klasy

Object Storage Arubacloud
0 głosów
292 wizyt
pytanie zadane 20 lipca 2023 w C i C++ przez Krzysztofs1234 Użytkownik (890 p.)
Cześć.

Problem przedstawię na przykładzie:

Załóżmy, że mam enum class Button o elementach kolejno: play, pause, stop. Następnie tworzę klasę Radio o polu button typu Button(z enum class). Teraz chciałbym, aby klasa Radio miała wyłączny dostęp do enum class Button .

Koniec przykładu.

 

Jak to uzyskać?

 

Potencjalne rozwiązania i powody, dla których ich nie wdrożyłem:

1. Definicja enum class Button wewnątrz klasy Radio.
Każdy obiekt będzie miał swoją klasę enum, tylko po co, skoro ma być tylko do odczytu?

2. "Ukrycie" klasy wyliczeniowej w przestrzeni nazw.
To chyba tak, jakbym zabezpieczył hasłem fragment kodu. Mam klasę Radio w pliku Radio.h, który załączam do main.cpp, by móc z niej korzystać. Jeśli poznam przestrzeń nazw, to będę mógł korzystać z enum class Button w main.cpp (a miała to robić tylko klasa Radio).

Dodam, że zależy mi na enum, ponieważ wygodnie jest się odnosić do przycisku po jego nazwie, np. if(button == Button::play).

Proszę o pomoc.

1 odpowiedź

+3 głosów
odpowiedź 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)

1. Definicja enum class Button wewnątrz klasy Radio.
Każdy obiekt będzie miał swoją klasę enum

Nie, "obiekt" nie ma "swojej klasy enum". Klasa jest jedna. W obiekcie w ogóle nie ma żadnych klas, w pamięci komputera każdy obiekt ma pola i tyle.

Więc jak enum ma być tylko dla klasy, to jak najbardziej po prostu zadeklaruj go w środku klasy.

komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)
Czyli mam rozumieć, że definiując enum class wewnątrz klasy, nie jest ona traktowana jako pole i będzie ona jedna, wspólna dla wszystkich instancji?
komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)
Tak. Ogólnie klasa, enum etc to są koncepty istniejące tylko dla kompilatora. One nigdy nie istnieją "jako pola", w ogóle nie istnieją jakie dane w skompilowanym programie. Klasa, enum etc tylko mówi kompilatorowi jak ułożyć dane w pamięci i jak nimi manipulować.
komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)
Ok. A załóżmy, że jest więcej klas, które korzystają z tej listy wyliczeniowej Button. Są przecież odtwarzacze MP3, programy odtwarzające dźwięk, piloty... Czy oprócz kopiowania definicji do wnętrza konkretnych klas jest opcja, żeby dać im dostęp ekskluzywny dostęp do enum class Button?
komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)

uhh, chyba czegoś nie rozumiem. Jeśli wiele klas używa tego enuma, to... nie brzmi to zbyt "ekskluzywnie". Czemu nie może być po prostu na zewnątrz?

Ale jak jest w środku klasy ale publiczny to przecież wciąż możesz go jak najbardziej używać, to tak jakby był namespace:

class Klasa {
public:
    enum class E { A, B, C };
};
void f(){
    auto e = Klasa::E::B;
}

 

komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)
edycja 20 lipca 2023 przez Krzysztofs1234

Ekskluzywny znaczy, że coś jest dla zamkniętej grupy. Nie widzę tu sprzeczności. Ale jeśli słowo powoduje niejasność, to ujmę to inaczej: chcę zapobiec ogólnodostępności.

Dlaczego enum nie może być na zewnątrz?
Jeśli umieszczę go w tym samym pliku co klasa, ale poza klasą, i ten plik załączę do main.cpp, to każdy ma dostęp do klasy enum, a obiecałem ten dostęp tylko konkretnym klasom.

 

class Klasa {
public:
    enum class E { A, B, C };
};
void f(){
    auto e = Klasa::E::B;
}

Ten fragment kodu nasunął mi pewną myśl. Jeśli korzystając z operatora zasięgu - auto e = Klasa::E::B; - sięgam do klasy, ale nie powoduję utworzenia obiektu, to chyba alternatywne rozwiązanie. Mógłbym przecież enum class ustawić jako prywatne i zadeklarować w tej klasie przyjaźń z innymi klasami.

komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)
Niby tak, choć mam wrażenie że potężnie kombinujesz. Ogólnie `friend` się stara jak najmniej unikać w C++ie. Co Ci broni mieć dokładnie tak jak wrzuciłem wyżej?
komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)

Co Ci broni mieć dokładnie tak jak wrzuciłem wyżej?

Specyfikator public 

komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)
OK, no... podtrzymuję że przekombinowujesz, ale jak chcesz.
komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)
Nie upieram się przy konkretnym rozwiązaniu, bo lubię kombinować. Upieram się przy takim rozwiązaniu, które spełnia kryteria, które, jak sądzę, jasno tu opisałem.

Uważasz, że przekombinowuję? To znaczy, że jest jakieś lepsze rozwiązanie, prawda?

Tak czy inaczej, dzięki za pomoc.
komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)

To znaczy, że jest jakieś lepsze rozwiązanie, prawda?

Uważam, że YAGNI.

Jasne że stosuje się enkapsulację i chowanie dostępu na różne sposoby, ale podejście typu "tych 18 klas ma dostęp do Button::State, a wszystkie inne mają dostęp do Buttona ale nie Button::State" to raczej przesada - szczególnie jeśli to zwykły prosty enum.

komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)

"tych 18 klas ma dostęp do Button::State, a wszystkie inne mają dostęp do Buttona ale nie Button::State" to raczej przesada 

Nie rozumiem. Co dokładnie masz tutaj na myśli?

komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)

Przepraszam, źle zapamiętałem przykład. W Twoim oryginalnym pytaniu to byłoby 

"tych 18 klas ma dostęp do Radio::Button, a wszystkie inne mają dostęp do Radio ale nie Radio::Button"

Tak mniej więcej zrozumiałem Twoje wcześniejsze wypowiedzi. To jest dosłownie to:

Mógłbym przecież enum class ustawić jako prywatne i zadeklarować w tej klasie przyjaźń z innymi klasami.

komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)

Mam wrażenie, że możemy się gdzieś nie rozumieć, więc zilustruję to, o czym pisałem:

HelpClass.h:

#ifndef HELPCLASS_H
#define HELPCLASS_H


class HelpClass
{
private:
    friend class Radio;
    friend class MP3;

    enum class Button
    {
        play,
        pause,
        stop
    };
};

#endif // HELPCLASS_H


Radio.h:

#ifndef RADIO_H
#define RADIO_H

#include "HelpClass.h"

class Radio
{
public:
    HelpClass::Button button;

    void setPlayButton()
    {
        button = HelpClass::Button::play;
    }

    HelpClass::Button getButton()
    {
        return button;
    }
};

#endif // RADIO_H


MP3.h:
 

#ifndef MP3_H
#define MP3_H

#include "HelpClass.h"

class MP3
{
public:
    HelpClass::Button button;

    void setPauseButton()
    {
        button = HelpClass::Button::pause;
    }

    HelpClass::Button getButton()
    {
        return button;
    }
};

#endif // MP3_H


main.cpp:
 

#include "Radio.h"
#include "MP3.h"


#include <iostream>


int main()
{
    //Button button = Button::play; //error: 'Button' was not declared in this scope, wiec nie jest tu znany enum class Button

    Radio radio;
    MP3 mp3;

    radio.setPlayButton();
    mp3.setPauseButton();

    std::cout << static_cast<short>(radio.getButton())
              << std::endl
              << static_cast<short>(mp3.getButton());

    return 0;
}


Co mam?
Mam jeden i tylko jeden typ wyliczeniowy. Mogę określić, jakie klasy mogą mieć do niego dostęp. Nic poza nimi nie ma do niego dostępu.
(choć trochę mało estetycznie używać dwukrotnie operatora zasięgu)

To właśnie miałem na myśli, pisząc:

A załóżmy, że jest więcej klas, które korzystają z tej listy wyliczeniowej Button. Są przecież odtwarzacze MP3, programy odtwarzające dźwięk, piloty... Czy oprócz kopiowania definicji do wnętrza konkretnych klas jest opcja, żeby dać im dostęp ekskluzywny dostęp do enum class Button?

komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)

OK, to podtrzymuję, że... nikt tak nie robi. Już pomijam że to jest przekombinowane i dziwne, ale nowsze języki by to nazwały wręcz błędnym.

Masz sobie klasę MP3, ma publiczną metodę... ale co z tego że jest publiczna, skoro nie możesz jej wywołać jeśli nie jesteś w ekskluzywnym klubie friendów który może stworzyć jej argument. To się nie skleja, stworzyłeś sobie własną sieć zależności którą będziesz musiał ręcznie zmieniać za każdym razem gdy dodasz nową klasę (a co dopiero wolnostojącą funkcję).

Współcześniejsze języki, jak taki Rust, w ogóle by Ci nie dały takiego pomysłu skompilować bo to nie ma sensu; dosłownie narzeka że jest publiczna metoda ale typ argumentu jest prywatny:

error[E0446]: private type `Button` in public interface
 --> <source>:8:5
  |
2 | enum Button {}
  | ----------- `Button` declared as private
...
8 |     pub fn setPauseButton(button: Button) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type

Jak chcesz żeby tylko jeden cały moduł kodu miał dostęp do typu to wybierz opcję 2 z Twojego posta, czyli schowaj w namespace'ie. Ale nie kombinuj z ręcznym wypisywaniem "autoryzowanych typów-przyjaciół"

komentarz 20 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)

Dzięki za rozjaśniające temat odpowiedzi.

Generalnie rzecz ujmując, czasami wybiegam w przyszłość i problem z ograniczeniem dostępu określonym klasom jest hipotetyczny. Zastanawiałem się, czy kiedyś, może nawet w niedalekiej przyszłości, spotkam się z takim problemem. Jak powinienem go wtedy rozwiązać? Tak jak realnie dziś spotkałem się z problemem, który opisałem w głównym wpisie.

komentarz 20 lipca 2023 przez adrian17 Ekspert (345,160 p.)

Zastanawiałem się, czy kiedyś, może nawet w niedalekiej przyszłości, spotkam się z takim problemem.

To w ogóle nie jest problem z którym się "spotyka" - kwestia dostępu do typów/interfejsów to coś na co programista decyduje się z własnej woli :) I tak się składa że po prostu ludzie zazwyczaj mają do tego mocno inne podejście niż wymyśliłeś.

komentarz 21 lipca 2023 przez TOWaD Mądrala (6,000 p.)
edycja 22 lipca 2023 przez TOWaD

A tak trochę głupot na początek weekendu.

protected by spełnił twoje wymagania, (tylko dziedzicenie zamiast przyjaźni).

tu -> działający i kompilujący się twój przykład.

ps. ale to tylko wypowiedz laika. Lepiej zastosować co adrian17 napisał.

Edit: :private HelpClass ?

komentarz 21 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)
Cześć.

Dzięki za uzupełnienie, faktycznie, nie wiem, czemu sam po prostu nie dałem protected.

Będąc w temacie, może tak naprawdę szukałem odpowiednika "javowego" interfejsu w C++?
komentarz 21 lipca 2023 przez adrian17 Ekspert (345,160 p.)

Dzięki za uzupełnienie, faktycznie, nie wiem, czemu sam po prostu nie dałem protected.

To... nijak nie rozwiązuje tematu "dostępu"/"prywatności", to też nie ma sensu. "Chowanie" nic nie robi dla klas, bo każda klasa może sobie dodać dziedziczenie jak chce. Za to wolne funkcje w ogóle nie mają fizycznie możliwości nigdy uzyskać dostępu, więc jak chcesz móc przekazać jakikolwiek argument typu Button z zewnątrz, albo wynik `getButton()` zapisać do zmiennej?

Mówię Ci poważnie, nigdzie w żadnych programach nie znajdziesz tak napisanego kodu; ludzie tak nie robią bo to sztuka dla sztuki, nic nie daje a tylko komplikuje.

komentarz 21 lipca 2023 przez Krzysztofs1234 Użytkownik (890 p.)

"Chowanie" nic nie robi dla klas, bo każda klasa może sobie dodać dziedziczenie jak chce.

Słuszna uwaga. 

Podobne pytania

0 głosów
1 odpowiedź 243 wizyt
pytanie zadane 20 grudnia 2017 w C i C++ przez Łukasz Wasilewski Mądrala (5,190 p.)
+1 głos
2 odpowiedzi 279 wizyt
pytanie zadane 25 grudnia 2020 w C i C++ przez TOWaD Mądrala (6,000 p.)
0 głosów
1 odpowiedź 501 wizyt
pytanie zadane 10 czerwca 2020 w C i C++ przez ResCrove Obywatel (1,700 p.)

92,626 zapytań

141,486 odpowiedzi

319,844 komentarzy

62,009 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!

...