Czy konstruktor C++, który łączy się ze sprzętem, powinien działać naprawdę? [duplikat]

Możliwy duplikat:
ile pracy należy wykonać w konstruktorze?

Zmagam się z pewną radą, którą mam z tyłu głowy, ale której nie pamiętam.

Wydaje mi się, że w pewnym momencie przeczytałem pewną radę (nie pamiętam źródła), że konstruktory C++ nie powinny wykonywać prawdziwej pracy. Raczej powinny inicjalizować tylko zmienne. Porada wyjaśniała, że prawdziwa praca powinna być wykonywana w jakiś metody init (), która zostanie wywołana oddzielnie po utworzeniu instancji.

Sytuacja jest taka, że mam klasę, która reprezentuje urządzenie sprzętowe. Logicznym sensem jest dla mnie wywołanie przez konstruktora procedur odpytywających urządzenie w celu zbudowania zmiennych instancji opisujących urządzenie. Innymi słowy, gdy new utworzy instancję obiektu, deweloper otrzyma obiekt, który jest gotowy do użycia, bez konieczności oddzielnego wywołania obiektu - >init ().

Czy jest dobry powód, dla którego konstruktorzy nie powinni wykonywać prawdziwej pracy? Oczywiście może to spowolnić czas alokacji, ale nie byłoby inaczej, gdyby wywołanie oddzielnej metody natychmiast po alokacji.

Po prostu staram się dowiedzieć, co gotchas obecnie nie biorąc pod uwagę, że może doprowadzić do takiej porady.

Author: Jim Hunziker, 2010-03-08

10 answers

Pamiętam, że Scott Meyers w bardziej efektywnym C++ zaleca, aby nie mieć zbędnego konstruktora domyślnego. W tym artykule poruszył również temat używania metod INIT () do "tworzenia" obiektów. Zasadniczo wprowadziłeś dodatkowy krok, który nakłada odpowiedzialność na klienta klasy. Ponadto, jeśli chcesz utworzyć tablicę wspomnianych obiektów, każdy z nich musiałby ręcznie wywołać INIT (). Możesz mieć funkcję Init, którą konstruktor może wywołać wewnątrz, aby zachować w przypadku implementacji Reset (), ale z doświadczeń lepiej jest usunąć obiekt i odtworzyć go, niż próbować resetować jego wartości do wartości domyślnych, chyba że obiekty są tworzone i niszczone wiele razy w czasie rzeczywistym (np. efekty cząsteczkowe).

Zauważ również, że konstruktory mogą wykonywać listy inicjalizacyjne, których normalne funkcje nie mogą.

Jednym z powodów, dla których można przestrzec przed używaniem konstruktorów do ciężkiej alokacji zasobów, jest to, że może być trudno wychwycić wyjątki w konstruktorach. Istnieją jednak sposoby obejścia tego problemu. W przeciwnym razie myślę, że konstruktory mają robić to, co mają robić-przygotowywać obiekt do jego początkowego stanu wykonania (ważne przy tworzeniu obiektu jest alokacja zasobów).

 26
Author: Extrakun,
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
2010-03-08 06:41:36

Jedynym powodem, aby nie wykonywać" pracy " w konstruktorze jest to, że jeśli zostanie wyrzucony wyjątek, Destruktor klasy nie zostanie wywołany. Ale jeśli używasz zasad RAII i nie polegasz na swoim destruktorze do sprzątania, to uważam, że lepiej nie wprowadzać metody, która nie jest wymagana.

 18
Author: Warpin,
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
2010-03-08 06:23:12

Zależy, co rozumiesz przez prawdziwą pracę. Konstruktor powinien umieścić obiekt w stanie używalnym, nawet jeśli stan ten jest flagą, co oznacza, że nie został jeszcze zainicjowany: -)

Jedynym uzasadnieniem, na jakie kiedykolwiek natknąłem się, aby nie wykonywać prawdziwej pracy, byłby fakt, że konstruktor może zawieść tylko z wyjątkiem (a Destruktor nie zostanie wywołany w tym przypadku). Nie ma możliwości zwrócenia ładnego kodu błędu.

Pytanie, które musisz sobie zadać jest:

Czy obiekt jest użyteczny bez wywołania metody init?

Jeśli odpowiedź brzmi "nie", wykonywałbym całą tę pracę w konstruktorze. W przeciwnym razie będziesz musiał złapać sytuację, w której użytkownik utworzył instancję, ale jeszcze nie zainicjował i zwrócić jakiś błąd.

Oczywiście, jeśli możesz ponownie zainicjalizować urządzenie, powinieneś podać jakąś metodę init, ale w takim przypadku, ja bym nadal wywołał tę metodę z konstruktor, jeśli powyższy warunek jest spełniony.

 9
Author: paxdiablo,
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
2010-03-08 06:28:43

Oprócz innych sugestii dotyczących obsługi wyjątków, jedną rzeczą, którą należy wziąć pod uwagę podczas łączenia się z urządzeniem sprzętowym, jest sposób, w jaki Klasa poradzi sobie z sytuacją, w której urządzenie nie jest obecne lub komunikacja zawodzi.

W sytuacji, gdy nie możesz komunikować się z urządzeniem, być może będziesz musiał podać kilka metod na swojej klasie, aby i tak wykonać późniejszą inicjalizację. W takim przypadku bardziej sensowne może być utworzenie instancji obiektu, a następnie uruchomienie przez inicjalizacja. Jeśli inicjalizacja nie powiedzie się, możesz po prostu zatrzymać obiekt i spróbować zainicjować komunikację ponownie w późniejszym czasie. Może być też konieczne zajęcie się sytuacją, w której komunikacja zostanie utracona po inicjalizacji. W obu przypadkach, prawdopodobnie będziesz chciał pomyśleć o tym, jak zaprojektujesz klasę do obsługi problemów komunikacyjnych w ogóle i to może pomóc Ci zdecydować, co chcesz zrobić w konstruktorze w porównaniu z metodą inicjalizacji.

When I ' ve zaimplementowane klasy, które komunikują się ze sprzętem zewnętrznym, łatwiej było utworzyć instancję "odłączonego" obiektu i podać metody łączenia i konfigurowania początkowego stanu. Ogólnie zapewnia to większą elastyczność łączenia/rozłączania/ponownego łączenia z urządzeniem.

 7
Author: Neal Stublen,
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
2010-03-08 07:08:57

Jedynym prawdziwym powodem jest Testowalność. Jeśli konstruktory są pełne "prawdziwej pracy", zwykle oznacza to, że obiekty mogą być tworzone tylko w ramach w pełni zainicjowanej, uruchomionej aplikacji. To znak, że obiekt / Klasa potrzebuje dalszego rozkładu.

 3
Author: Alex R,
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
2010-03-08 06:29:47

Podczas używania konstruktora i metody Init() masz źródło błędu. Z mojego doświadczenia wynika, że napotkasz sytuację, w której ktoś zapomni go nazwać, a Ty możesz mieć subtelny błąd w rękach. Powiedziałbym, że nie powinieneś robić zbyt wiele pracy w swoim konstruktorze, ale jeśli potrzebna jest jakakolwiek metoda init, to masz nietrywialny scenariusz budowy i najwyższy czas przyjrzeć się wzorom tworzenia. Funkcja budowniczego lub fabryka być mądry, aby spojrzeć na. Z prywatnym konstruktorem upewniając się, że nikt poza fabryką lub funkcją budowniczego nie buduje obiektów, dzięki czemu możesz mieć pewność, że są one zawsze poprawnie skonstruowane.

Jeśli twój projekt pozwala na błędy w realizacji, ktoś to zrobi. Mój kolega Murphy mi to powiedział;)

W mojej dziedzinie pracujemy z wieloma podobnymi sytuacjami sprzętowymi. Fabryki dają nam zarówno testowalność, bezpieczeństwo, jak i lepsze sposoby na niepowodzenie budowy.

 3
Author: daramarak,
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
2010-03-08 09:29:07

Warto zastanowić się nad problemami życiowymi i połączeniem/ponownym połączeniem, jak wskazuje Neal S.

Jeśli nie połączysz się z urządzeniem na drugim końcu łącza, często zdarza się, że "urządzenie" na Twoim końcu jest użyteczne i będzie później, jeśli drugi koniec połączy się. Przykładami są połączenia sieciowe itp.

Z drugiej strony, jeśli spróbujesz uzyskać dostęp do jakiegoś lokalnego urządzenia sprzętowego, które nie istnieje i nigdy nie będzie istniało w ramach Twojego programu (dla przykład karta graficzna, która nie jest obecna) to myślę, że jest to przypadek, w którym chcesz wiedzieć to w konstruktorze, aby konstruktor mógł rzucać i Obiekt nie może istnieć. Jeśli tego nie zrobisz, możesz skończyć z obiektem, który jest nieprawidłowy i zawsze taki będzie. Wrzucanie konstruktora oznacza, że obiekt nie będzie istniał, a zatem funkcje nie mogą być wywoływane na tym obiekcie. Oczywiście musisz być świadomy problemów z czyszczeniem, jeśli dorzucisz konstruktor, ale jeśli nie w przypadkach w ten sposób zazwyczaj kończy się sprawdzaniem poprawności we wszystkich funkcjach, które mogą być wywołane.

Więc myślę, że powinieneś zrobić wystarczająco dużo w konstruktorze, aby upewnić się, że masz poprawny, użyteczny obiekt utworzony.

 2
Author: Michael,
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
2010-03-08 12:11:01

Chciałbym tam dodać własne doświadczenie.

Nie powiem zbyt wiele o tradycyjnym konstruktorze debaty/Init... na przykład Google wytyczne odradzają cokolwiek w Konstruktorze, ale to dlatego, że odradzają wyjątki i 2 współpracują ze sobą.

Mogę mówić o Connection klasie, której używam.

Kiedy klasa Connection zostanie utworzona, spróbuje połączyć się (przynajmniej, jeśli nie domyślnie skonstruowana). Jeśli Connection nie powiedzie się... na obiekt jest nadal zbudowany i nie wiesz o tym.

Kiedy próbujesz użyć klasy Connection jesteś więc w jednym z 3 przypadków:

  • żaden parametr nigdy nie został sprecyzowany > wyjątek lub kod błędu
  • obiekt jest rzeczywiście podłączony > fine
  • obiekt nie jest podłączony, spróbuje połączyć > to się powiedzie, dobrze, to się nie powiedzie, otrzymasz wyjątek lub kod błędu
Myślę, że warto mieć oba. Oznacza to jednak, że w każda pojedyncza metoda korzystająca z połączenia, musisz sprawdzić, czy działa.

Warto jednak ze względu na zdarzenia rozłączenia. Po nawiązaniu połączenia możesz utracić połączenie bez wiedzy obiektu. Zamykając samoczynne Sprawdzanie połączenia w metodę reconnect, która jest wywoływana wewnętrznie przez wszystkie metody wymagające działającego połączenia, naprawdę izolujesz deweloperów od radzenia sobie z problemami... albo przynajmniej tyle ile możesz od kiedy wszystko zawodzi nie masz innego rozwiązania, które daje im znać:)

 1
Author: Matthieu M.,
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
2010-03-08 12:26:14

Wykonywanie "prawdziwej pracy" w konstruktorze jest najlepiej unikać.

Jeśli skonfiguruję połączenia z bazą danych, otwieram pliki itp. wewnątrz konstruktora i jeśli w ten sposób jeden z nich wywoła wyjątek, doprowadzi to do wycieku pamięci. Spowoduje to zagrożenie bezpieczeństwa WYJĄTKÓW Twojej aplikacji .

Innym powodem unikania pracy w konstruktorze jest to, że sprawiłoby to, że Twoja aplikacja byłaby mniej testowalna. Załóżmy, że piszesz procesor płatności kartą kredytową. If say in CreditCardProcessor class ' S konstruktor wykonujesz wszystkie prace związane z podłączeniem do bramki płatniczej, autoryzacją i obciążeniem karty kredytowej Jak napisać testy jednostkowe dla klasy CreditCardProcessor?

Idąc do twojego scenariusza, jeśli procedury, które odpytują urządzenie, nie wywołują żadnych WYJĄTKÓW i nie zamierzasz testować klasy w izolacji, to prawdopodobnie jest jej lepiej wykonywać pracę w konstruktorze i unikać wywołań tej dodatkowej metody init.

 0
Author: ardsrk,
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
2010-03-08 13:12:18

Jest kilka powodów, dla których użyłbym oddzielnego konstruktora / init ():

  • leniwa / opóźniona inicjalizacja. Pozwala to na szybkie utworzenie obiektu, szybką reakcję użytkownika i opóźnienie dłuższej inicjalizacji w celu późniejszego lub przetworzenia w tle. Jest to również część jednego lub więcej wzorców projektowych dotyczących pul obiektów wielokrotnego użytku, aby uniknąć kosztownej alokacji.
  • Nie wiem, czy ma on właściwą nazwę, ale być może podczas tworzenia obiektu informacja inicjalizacyjna jest niedostępne lub niezrozumiałe dla osoby tworzącej obiekt (na przykład masowe ogólne tworzenie obiektu). Inna część kodu posiada know-how, aby ją zainicjować, ale nie utworzyć.
  • Jako osobisty powód, Destruktor powinien być w stanie cofnąć wszystko, co zrobił konstruktor. Jeśli wymaga to użycia wewnętrznej metody init/deinit (), nie ma problemu, o ile są one wzajemnie lustrzanymi odbiciami.
 0
Author: Ioan,
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
2010-03-08 14:00:31