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 są 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ą.
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.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.
- liczby całkowite są okropnym typem danych waluty
- wpłata powinna być funkcją 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.
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.
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;)
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.
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;
}
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 .
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.
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.
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ń.
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ć.
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
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++.
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.
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