Czy funkcje get I set są popularne wśród programistów C++?

Pochodzę ze świata C# i uczę się C++. Zastanawiałem się nad funkcjami get I set W C++. W C# korzystanie z nich jest dość popularne, a narzędzia takie jak Visual Studio promują ich użycie, czyniąc je bardzo łatwymi i szybkimi do wdrożenia. Jednak wydaje się, że nie ma to miejsca w świecie C++.

Oto kod C # 2.0:

public class Foo
{
    private string bar;

    public string Bar
    {
        get { return bar; }
        set { bar = value; }
    }
}

Lub, w C # 3.0:

public class Foo { get; set; }

Niech ludzie powiedzą: Cóż w tym sens? Dlaczego po prostu nie utworzyć pola publicznego, a następnie zrób to nieruchomość później, jeśli trzeba; szczerze mówiąc, nie jestem pewien. Po prostu robię to z dobrej praktyki, ponieważ widziałem to tak wiele razy.

Teraz, ponieważ jestem tak przyzwyczajony do robienia tego, czuję, że powinienem przenieść ten nawyk do mojego kodu C++, ale czy to naprawdę konieczne? Nie widzę tego tak często jak w C#.

Tak czy siak, Oto C++ z tego co zbieram:

class Foo
{
public:
    std::string GetBar() const; // Thanks for the tip Earwicker.
    void SetBar(std::string bar);
private:
    std::string bar;
}

const std::string Foo::GetBar()
{
    return bar;
}

void Foo::SetBar(std::string bar)
{
    // Also, I always wonder if using 'this->' is good practice.
    this->bar = bar;
}

Teraz, dla mnie to wygląda na dużo pracy nóg; biorąc pod uwagę korzystanie z narzędzi Visual Studio, C# implementacja zajęłaby dosłownie kilka sekund, a pisanie C++ Zajęło mi dużo więcej czasu - uważam, że nie jest to warte wysiłku, zwłaszcza gdy alternatywa ma długość 5 linii: {]}

class Foo
{
public:
    std::string Bar;
}

Z tego co wiem, to są zalety:

  • Możesz zmienić szczegóły implementacji funkcji get I set, więc zamiast zwracać prywatne pole możesz zwrócić coś ciekawszego.
  • możesz później usunąć get/set i uczynić go tylko do odczytu/zapisu (ale jak na publiczny interfejs, wydaje się, że nie jest to dobre).

I wady:

    Czy to naprawdę jest warte wysiłku? Ogólnie rzecz biorąc. W niektórych przypadkach zalety sprawiają, że jest to warte wysiłku, ale mam na myśli mówiąc w kategoriach "dobrych praktyk", prawda?

Odpowiedź:

Dlaczego wybrałemodpowiedź z mniejszą liczbą głosów ? W rzeczywistości byłem bardzo blisko wyboru odpowiedzi veefu ; jednak moje osobiste opinia (która jest pozornie kontrowersyjna), jest taka, że odpowiedź nad jajkiem budyń.

Odpowiedź, którą wybrałem, z drugiej strony wydaje się argumentować obie strony; myślę, że gettery i settery złe, jeśli są używane nadmiernie (mam przez to na myśli, gdy nie jest to konieczne i łamie model biznesowy), ale dlaczego nie mielibyśmy mieć funkcji o nazwie GetBalance()?

Z pewnością byłoby to o wiele bardziej wszechstronne niż PrintBalance(); A gdybym chciał pokazać to użytkownikowi w inny sposób niż jako klasa chciałeś? W pewnym sensie GetBalance() może nie być wystarczająco istotne, aby argumentować, że "gettery i settery są dobre", ponieważ nie ma (a może nie powinno) towarzyszącego setterowi, a mówiąc o tym, funkcja o nazwie SetBalance(float f) może być zła (moim zdaniem), ponieważ sugerowałaby implementatorowi funkcji, że konto musi być manipulowane poza klasą, co nie jest dobrą rzeczą.

Author: Community, 2009-04-10

14 answers

Argumentowałbym, że dostarczanie accesorów jest ważniejsze w C++ niż w C#.

C++ nie ma wbudowanej obsługi właściwości. W C# możesz zmienić publiczne pole na właściwość głównie bez zmiany kodu użytkownika. W C++ jest to .

Dla mniejszego pisania możesz zaimplementować trywialne settery / gettery jako metody inline:

class Foo
{
public:
    const std::string& bar() const { return _bar; } 
    void bar(const std::string& bar) { _bar = bar; } 
private:
    std::string _bar;
};
I nie zapominaj, że getterzy i setterzy są nieco źli.
 30
Author: mfazekas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-05 20:43:01

Ryzykując, że będę kłótliwy, poprę przeciwny punkt widzenia, z którym zetknąłem się po raz pierwszy podczas czytania "Holub on Patterns". Był to punkt widzenia, który był bardzo trudny, ale miał dla mnie sens po namyśle: {]}

Getters and Setters are Evil

Użycie getterów i setterów jest w opozycji do podstaw projektowania obiektowego: abstrakcji danych i enkapsulacji. Nadużywanie getterów i seterów sprawi, że Twój kod będzie mniej zwinny i możliwy do utrzymania w na dłuższą metę. Ostatecznie ujawniają podstawową implementację klasy, blokując szczegóły implementacji w interfejsie klasy.

Wyobraź sobie, że Twoje pole 'std:: string Foo:: bar' wymaga zmiany z std:: string na inną klasę string, która, powiedzmy, jest lepiej zoptymalizowana lub obsługuje inny zestaw znaków. Będziesz musiał zmienić pole prywatnych danych, getter, setter i cały kod Klienta tej klasy, który wywołuje te gettery i settery.

Zamiast Zaprojektuj swoje klasy tak, aby "dostarczały dane" i "odbierały dane", zaprojektuj je tak, aby "wykonywały operacje" lub "świadczyły usługi". Zadaj sobie pytanie, dlaczego piszesz funkcję "GetBar". Co robisz z tymi danymi? Być może wyświetlasz te dane lub przetwarzasz je. Czy ten proces jest lepiej eksponowany jako metoda Foo?

To nie znaczy, że getterzy i setterzy nie mają swojego celu. W C# uważam, że podstawowym powodem ich użycia jest interfejs z Visual Studio GUI-design IDE, ale jeśli znajdziesz się pisząc je w C++, to prawdopodobnie najlepiej zrobić krok w tył, spojrzeć na swój projekt i zobaczyć, czy czegoś brakuje. [[3]}postaram się stworzyć przykład do zilustrowania.
// A class that represents a user's bank account
class Account {
  private:
    int balance_; // in cents, lets say 
  public:
    const int& GetBalance() { return balance_; }
    void SetBalance(int b) { balance_ = b; }
};

class Deposit {
  private:
    int ammount_;
  public:
    const int& GetAmount() { return ammount_; }
    void SetAmmount(int a) { _balance = a; }
};

void DoStuffWithAccount () {
  Account a;
  // print account balance
  int balance = a.GetBalance();
  std::cout << balance;

  // deposit some money into account
  Deposit d(10000);
  a.SetBalance( a.GetBalance() + d.GetValue());
}

Nie trzeba długo, aby zobaczyć, że jest bardzo źle zaprojektowany.

  1. liczby całkowite są okropnym typem danych waluty
  2. wpłata powinna być funkcją konta
[[3]}gettery i settery utrudniają naprawę problemy, ponieważ kod klienta DoStuffWithAccount jest teraz związany z typem danych, którego użyliśmy do wdrożenia salda konta.

Więc, zróbmy przejście na ten kod i zobaczyć, co możemy poprawić

// A class that represents a user's bank account
class Account {
  private:
    float balance_;
  public:
    void Deposit(float b) { balance_ += b; }
    void Withdraw(float w) { balance_ -= w; }
    void DisplayDeposit(std::ostream &o) { o << balance_; }
};

void DoStuffWithAccount () {
  Account a;
  // print account balance
  a.DisplayBalance(std::cout);

  // deposit some money into account
  float depositAmt = 1000.00;
  a.Deposit(depositAmt);
  a.DisplayBalance(std::cout);
}

'float' jest krokiem we właściwym kierunku. Można było zmienić wewnętrzny typ na 'float' i nadal obsługiwać idiom getter / setter:

class Account {
  private:
    // int balance_; // old implementation
    float balance_; 
  public:
    // support the old interface
    const int& GetBalance() { return (int) balance_; }
    void SetBalance(int b) { balance_ = b; }
    // provide a new interface for the float type
    const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int??
    void SetBalance(float b) { balance_ = b; }
};

Ale nie trzeba długo zdawać sobie sprawy, że układ getter / setter podwaja twoje obciążenie pracą i komplikuje ma znaczenie, ponieważ musisz obsługiwać zarówno kod, który używał ints, jak i nowy kod, który będzie używał pływaków. Funkcja wpłaty ułatwia nieco rozszerzenie zakresu typów wpłaty.

Klasa podobna do konta nie jest prawdopodobnie najlepszym przykładem, ponieważ" uzyskanie " salda konta jest naturalną operacją dla konta. Ogólnie rzecz biorąc, musisz być ostrożny z getterami i seterami. Nie popadaj w nawyk pisania getterów i seterów dla każdego członka danych. On dość łatwo zdemaskować i zablokować się w implementacji, jeśli nie jesteś ostrożny.

 34
Author: veefu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 16:49:06

W twoim przykładzie:

class Foo
{
public:
    const std::string GetBar(); // Should this be const, not sure?

Prawdopodobnie masz na myśli to:

std::string GetBar() const;

Umieszczenie const na końcu oznacza, że "ta funkcja nie modyfikuje wywołanej instancji Foo" , więc w pewien sposób oznacza ją jako czysty getter.

Czyste gettery występują często w C++. Przykładem w std::ostringstream jest funkcja str(). Biblioteka Standardowa często podąża za wzorcem używania tej samej nazwy funkcji dla pary funkcji getter/setter - str jest znowu przykładem.

Czy to zbyt dużo pracy pisać, i czy warto - to wydaje się dziwne pytanie! Jeśli chcesz dać klientom dostęp do niektórych informacji, podaj getter. Jeśli nie, to nie.

 11
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 12:02:46

[edytuj] wydaje się, że muszę podkreślić, że setery muszą walidować parametry i wymuszać niezmienniki, więc zwykle nie są tak proste, jak tutaj. [/edytuj]


Nie ze wszystkimi, ponieważ dla dodatkowego typowania. Mam tendencję do korzystania z nich znacznie częściej teraz, gdy asysta wizualna daje mi "enkapsulate field".

Legwork nie jest bardziej, jeśli zaimplementujesz tylko domyślne settery / gettery inline w deklaracji klasy (co zwykle robię - bardziej złożone settery przenoszą się do ciała, chociaż).

Niektóre Uwagi:

Konsternacja: Tak, getter powinien być const. Nie ma sensu, aby wartość zwracana const, chociaż, jeśli zwracasz przez wartość. W przypadku potencjalnie złożonych wartości zwracanych możesz użyć const & though:

std::string const & GetBar() const { return bar; } 

Setter chaining: wielu programistów lubi modyfikować setter jako taki:

Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; }

Który umożliwia wywołanie wielu seterów jako takich:

Foo foo;
foo.SetBar("Hello").SetBaz("world!");
Nie jest powszechnie akceptowane jako dobra rzecz, chociaż.

__declspec(własność): Visual C++ dostarcza to niestandardowe rozszerzenie, dzięki czemu osoby wywołujące mogą ponownie używać składni właściwości. Zwiększa to nieco pracę nóg w klasie, ale sprawia, że kod rozmówcy wygląda bardziej przyjaznie.


Podsumowując, jest trochę więcej pracy, ale garść decyzji do podjęcia w C++. Typowe;)
 7
Author: peterchen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 08:07:26

Nie ma w tym żadnej ścisłej konwencji, tak jak w C# czy Javie. Wielu programistów C++ po prostu upubliczniłoby zmienną i zaoszczędziłoby sobie kłopotów.

Jak mówiły inne odpowiedzi, nie powinieneś często potrzebować metod set I do pewnego stopnia get.

Ale jeśli i kiedy je zrobisz, nie ma potrzeby wpisywania więcej niż jest to konieczne:

class Foo
{
public:
    std::string Bar() const { return bar; }
    void Bar(const std::string& bar) { this->bar = bar; }
private:
    std::string bar;
};

Zadeklarowanie funkcji inline w klasie oszczędza typowanie i podpowiada kompilatorowi, że chcesz, aby funkcje inlined. I nie jest to dużo bardziej typowanie niż odpowiedniki C#. Należy zauważyć, że usunąłem prefiksy get / set. Zamiast tego mamy tylko dwa przeciążenia Bar (). Jest to dość powszechne w C++ (w końcu, jeśli nie bierze żadnych argumentów, wiemy, że jest to getter, a jeśli bierze argument, jest to setter. Nie potrzebujemy nazwy, aby nam to powiedzieć), a to oszczędza trochę więcej pisania.

 6
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 20:19:51

Prawie nigdy nie używam getterów i seterów w moim własnym kodzie. Odpowiedź Veefu wygląda mi dobrze.

Jeśli nalegasz na posiadanie getterów i / lub setterów, możesz użyć makr do cięcia na płycie kotła.

#define GETTER(T,member) const T& Get##member() const { return member; }
#define SETTER(T,member) void Set##member(const T & value) { member = value; }

class Foo
{
public:
    GETTER(std::string, bar)
    SETTER(std::string, bar)
private:
    std::string bar;
}
 4
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:08

Pobieranie i ustawianie członków danych qua data members: Bad .
Pobieranie i ustawianie elementów abstrakcji: dobre .

 3
Author: dmckee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 16:08:02

Argumenty przeciwko Get/Set pod względem projektowania API w przykładzie bankowym są trafne. Nie ujawniaj pól lub właściwości, jeśli pozwalają one użytkownikom łamać reguły biznesowe.

Jeśli jednak zdecydujesz, że potrzebujesz pola lub właściwości, Zawsze używaj właściwości.

Właściwości automatyczne w c# są bardzo łatwe w użyciu i istnieje wiele scenariuszy (databinding, serialization, etc), które nie działają z polami, ale wymagają właściwości.

 3
Author: Jason Coyne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 20:23:59

Jeśli tworzysz komponenty COM to tak, jest to Bardzo popularne.

 1
Author: Otávio Décio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 12:03:15

Get and set to ból zadawany ludziom, jeśli musisz używać ich w dowolnym języku.

Eiffel ma to dużo lepiej, gdzie wszystko, co różni się od ilości informacji, które musisz podać, aby uzyskać odpowiedź - funkcja z 0 parm jest taka sama jak dostęp do zmiennej członkowskiej i można dowolnie zmieniać między nimi.

Kiedy kontrolujesz obie strony interfejsu, definicja interfejsu nie wydaje się być tak dużym problemem. Jednak gdy chcesz zmienić szczegóły implementacji i wymaga rekompilacji kodu klienta, jak to jest w C++ powszechnym przypadku chcesz być w stanie zminimalizować to jak najwięcej. Jako takie pImpl I get/set będą częściej używane w publicznych API, aby uniknąć takich uszkodzeń.

 1
Author: Greg Domjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 13:39:13

Metody Get I Set są użyteczne, jeśli w wartości zmiennej występują ograniczenia. Na przykład w wielu modelach matematycznych istnieje ograniczenie utrzymywania pewnej zmiennej zmiennoprzecinkowej w zakresie [0,1]. W tym przypadku, Get I Set (specjalnie Set) mogą odegrać ładną rolę:

class Foo{
public:
    float bar() const { return _bar; } 
    void bar(const float& new_bar) { _bar = ((new_bar <= 1) && (new_bar >= 0))?new_bar:_bar; } // Keeps inside [0,1]
private:
    float _bar;     // must be in range [0,1]
};

Również niektóre właściwości muszą zostać przeliczone przed odczytaniem. W takich przypadkach ponowne obliczenie każdego cyklu może zająć dużo niepotrzebnego czasu obliczeniowego. Tak więc sposobem na jego optymalizację jest przeliczanie tylko podczas odczytu zamiast tego. W tym celu należy przeciążyć metodę Get, aby zaktualizować zmienną przed jej odczytaniem.

W przeciwnym razie, jeśli nie ma potrzeby walidacji wartości wejściowych lub aktualizacji wartości wyjściowych, upublicznienie właściwości nie jest przestępstwem i możesz się z tym zgodzić.

 1
Author: Amyr Borges Fortes Neto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 13:54:40

Jeśli używasz C++ / CLI jako odmiany C++, to ma natywną obsługę właściwości w języku, więc możesz użyć

property String^ Name;

To to samo co

String Name{get;set;}

W C#. Jeśli potrzebujesz bardziej precyzyjnej kontroli nad metodami get/set, możesz użyć

property String^ Name
{
   String^ get();
   void set(String^ newName);
}

W nagłówku i

String^ ClassName::Name::get()
{
   return m_name;
}

void ClassName::Name::set(String^ newName)
{
   m_name = newName;
}

W .plik cpp. Nie pamiętam z ręki, ale myślę, że możesz mieć różne uprawnienia dostępu dla metod get I set (public/private etc).

Colin

 0
Author: Colin Desmond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-21 12:17:03

Tak , get I set są popularne w świecie c++.

 -1
Author: user88637,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 13:03:51

Kompilator będzie emitował set_ i get_ jeśli zdefiniujesz właściwość, więc tak naprawdę jest to po prostu oszczędność typowania.

To była ciekawa dyskusja. To coś z mojej ulubionej książki " CLR via C#".

Oto co zacytowałem.

Osobiście nie lubię nieruchomości i żałuję, że nie wspierane w Microsoftm.NET Framework i jego programowanie języki. Powodem jest to, że właściwości wyglądają jak pola, ale one są metody. Wiadomo, że powodują fenomenalną ilość confu-sion. Gdy programista widzi kod, który wydaje się mieć dostęp do pole, istnieje wiele założeń że programista sprawia, że może nie być prawdą dla nieruchomości. Na przykład,

  • właściwość może być tylko do odczytu lub tylko do zapisu; dostęp do pola jest zawsze
    czytelny i zapisywalny. Jeśli zdefiniujesz
    nieruchomość, najlepiej jest zaoferować zarówno
    get I set accessor methods.
  • A metoda właściwości może rzucać wyjątek; field access never throws
    wyjątek.

  • Właściwość nie może być przekazywana jako parametr out lub ref do metoda; pole może.

  • Wywołanie metody właściwości może zająć dużo czasu; dostęp do pola zawsze
    / align = "left" / A common
    powodem użycia właściwości jest
    wykonać synchronizację wątków, która może zatrzymać wątek na zawsze i
    dlatego, nieruchomość nie powinna być
    używane, gdy synchronizacja wątku jest
    wymagane. W takiej sytuacji metoda jest preferowany. Również, jeśli twoja klasa może być dostępne zdalnie (na przykład
    twoja klasa pochodzi z
    System.MashalByRefObject), wywołanie
    metoda właściwości będzie bardzo
    powolne, a zatem metoda jest
    preferowany obiekt. W moim
    opinie, klasy pochodzące z
    MarshalByRefObject nie powinien nigdy używać
    właściwości.

  • Jeśli wywołana jest wiele razy z rzędu, metoda właściwości może zwrócić
    za każdym razem inna wartość; a
    pole zwraca tę samą wartość co
    czas. System.Klasa DateTime ma właściwość teraz tylko do odczytu, która zwraca
    aktualna data i godzina. Za każdym razem zapytasz tę właściwość, będzie
    zwraca inną wartość. To jest
    błąd, a Microsoft życzy sobie, aby
    mogą naprawić klasę, wykonując
    Teraz metoda zamiast właściwości.

  • Metoda właściwości może powodować obserwowalne skutki uboczne; dostęp do pola nigdy nie ma. Innymi słowy, użytkownik Typ powinien być w stanie ustawić różne
    właściwości zdefiniowane przez typ w dowolnym
    zamówienie wybiera bez
    zauważanie różnych zachowań w
    Typ.

  • metoda właściwości może wymagać dodatkowej pamięci lub zwracać
    odniesienie do czegoś, co nie jest
    faktycznie część stanu obiektu, tak więc modyfikacja zwracanego obiektu ma
    brak wpływu na oryginalny obiekt;
    Zapytanie o Pole zawsze zwraca
    odniesienie do obiektu
    gwarancja bycia częścią oryginału stan obiektu. Praca z
    właściwością zwracającą kopię może być
    bardzo mylące dla programistów i
    cecha ta często nie udokumentowane.
 -3
Author: J.W.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-10 13:13:39