Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

C++ (AVR GCC) - Dynamiczne tworzenie obiektów

czarusgg 02 Feb 2016 12:06 3336 34
Computer Controls
  • #1
    czarusgg
    Level 12  
    Witam wszystkich!

    Jak stworzyć dynamicznie obiekt reprezentowany przez wskaźnik na tenże obiekt?

    Sedno problemu polega na tym, że mam klasę. Zdefiniowany wskaźnik na obiekt tej klasy zainicjowany wartością NULL.

    W trakcie życia programu chcę w pewnym momencie powołać do życia obiekt, wykorzystać, po czym zniszczyć. Nie chcę marnować pamięci (a na razie tak robię) poprzez globalną zmienną statyczną o typie mojej klasy.

    Oto przykłady rozjaśniające trochę moje mgliste tłumaczenia :)

    Code: c
    Log in, to see the code


    Oczywiście ostatnie 3 linijki to abstrakcja (z PC), ale właśnie czegoś takiego potrzebuję w swoim programie.

    Tworzę sobie bibliotekę do obsługi różnych peryferiów (USART) i chciałbym bufory wejściowe/wyjściowe przy otwieraniu nadanika/odbiornika powoływac do życia (klasa CFIFO) a niszczyć przy zamykaniu nadajnika/odbiornika, po co mają zalegać w pamięci, gdy dany USART jest nie uzywany?[/code]
  • Computer Controls
  • #2
    pawel1029384756
    Level 21  
    Pamiętam, że kiedyś już z czymś takim walczyłem dla samej rozrywki, ale po wielu godzinach bez efektów się poddałem. Prawdopodobnie, że nie da się tworzyć obiektów w ten sposób, z klasami nie ma problemu. Ale już nie pamiętam z czym dokładnie miałem w tedy problem. Po szybkim przeszukaniu googli z hasłem "zmienne dynamiczne avr-g++" nie wyszukuje nic konkretnego, nawet w angielskojęzycznej części internetów.
    Ogólnie to polecam robić takie rzeczy przy pomocy struktur, które w C++ mogą także zawierać funkcję, ale jest to lepiej optymalizowane.
  • #3
    czarusgg
    Level 12  
    Tak, problem polega na tym, ze w AVR GCC w ogóle nie istnieją operatory new i delete. Wszystko do czego się dokopałem, to rady, by zdefiniować sobie makro bądź funkcję, która new zamieni na malloc a delete na free.

    Tylko że mi zależy na obiekcie a nie ciągu bajtów o wielkości takiego obiektu w pamięci :(
  • Helpful post
    #5
    grko
    Level 33  
    @pawel1029384756
    Quote:

    Ogólnie to polecam robić takie rzeczy przy pomocy struktur, które w C++ mogą także zawierać funkcję, ale jest to lepiej optymalizowane.


    Struktury od klas w C++ różnią się tylko domyślnym dostępem do pól. W klasie domyślnie pola są private, w strukturze public.
  • #6
    czarusgg
    Level 12  
    kijas1 czytałem już ten wątek, niestety, jest to tylko alokacja pamięci (nadaje się do dynamicznych tabel ale nie obiektów) :( Niestety. Tabela taka też musi składać się z typów prostych, ewentualnie struktur, ale niestety nie obiektów, ponieważ dochodzi tylko do zarezerwowania pamięci potrzebnej na pomieszczenie składowych obiektu, ale nie powołuje obiektu do życia...

    Aczkolwiek...

    Ok. Rozwiązanie połowiczne, dla potomnych, chyba, że ktoś rozwinie sprawę o konstruktory...

    Zarezerwowanie miejsca w pamięci za pomocą malloc prawie czyni obiekt. To znaczy, obiektu nie ma, ale jest zarezerwowany obszar pamięci na stercie. Wszystkie metody działaja prawidłowo z takim wskaźnikiem (to akurat dość logiczne).

    Czyli da się wykonać z mojego przykładu sekwencję: BUF->Metoda (15). Gdy BUF wskazuje na zarezerwowaną pamięć. Informacja o typie wskaźnika pozwala kompilatorowi na prawidłowe kojarzenie metod z obszarem pamięci na którym operują (offset do pola względem początku obiektu). Nie mniej jednak nie jest to obiekt a nadal tylko fragment pamięci ze składowymi "obiektu". Co za tym idzie, nie jest ani wywoływany konstruktor automatycznie, ani destruktor podczas wykonywania free. Konstruktora nie da się uruchomić jak metody (BUF->Klasa (10)).

    Oczywiście w tej sytuacji akcje z konstruktora trzeba przenieść do jakiejś metody np. Init i wołać ta metodę z ciała konstruktora.

    W ten sposób obiekt zostanie dobrze stworzony gdy mamy zmienna typu TKlasa a dla wskaźników z późniejszym allokowaniem w pamięci, trzeba ręcznie wywołać metodę Init. Ale to już chyba jest drobnostka...

    Oczywiście w tej sytuacji trochę psuje się enkapsulacja. Jeśli ja w takim obiekcie mam wskaźniki i konstruktor alokuje pamięć, to kilkukrotne wywołanie metody Init (jest przecież publiczna) powoduje wycieki pamięci. Ale z drugiej strony jak na razie lepszego rozwiązania nie znam a swoje programy pisze sam i samokontrola powinna wystarczyć, do tego by uzywać Init tylko po wykonaniu malloc i np. Destroy tylko przed użyciem free :) Gorzej jak taka klasa trafi do "innych" :)

    Dodano po 13 [minuty]:

    GrzegorzKostka Spróbuję, dzięki za trop. Czy funkcje definiuje się tak samo jak metody?

    Dodano po 30 [sekundy]:

    I co z konstruktorami i destruktorami dla struktur?

    Dodano po 6 [minuty]:

    Ok. Spróbowałem. W zasadzie absolutnie niczym się to nie różni działa dokładnie tak samo, nawet uruchamiany jest konstruktor dla takiej "obiektowej" struktury :)

    Dodano po 1 [minuty]:

    Zamykam temat jako rozwiazany, dziękuję kijas1 oraz GrzegorzKostka za pomoc, pomogliście.
  • #7
    tmf
    Moderator of Microcontroller designs
    Jako, że wskazany na AVRFreaks temat został założony przeze mnie, dodam tylko, że pokazane tam definicje operatorów new i delete zapewniają alokację i inicjalizację obiektu (wywołanie konstruktora lub destruktora. Także wystarcza to do pełnej implementacji obiektów z C++.
  • Computer Controls
  • #8
    szczywronek
    Level 27  
    Jako, że temat znowu jest otwarty, pozwolę sobie wysłać to, czego wcześniej nie zdążyłem ;)

    -----
    @czarusgg - wydaje mi się, że się mylisz (ale tylko wydaje, bom kiepski z C++) ;) Przeładowując operator new masz możliwość zmiany tylko jego "części" odpowiedzialnej za przydzielenie pamięci. Wywołanie konstruktora następuje z automatu. Tak samo jest z delete i destruktorem. Sprawdziłem dla pewności (proszę nie doszukiwać się sensu w kodzie, to tylko przykład na szybko):
    Code: c
    Log in, to see the code

    Zarówno konstruktor jak i destruktor zostały uruchomione:
    Code: avrasm
    Log in, to see the code
  • #9
    BlueDraco
    MCUs specialist
    Taaak, AVR o niczym innym nie marzy, niż o dynamicznej alokacji obiektów w swej potężnej pamięci. Może jeszcze metody wirtualne, żeby go do końca dorżnąć, co?
  • #10
    alagner
    Level 26  
    Dlaczego nie, state machine pattern działa świetnie :)
  • #11
    kijas1
    Level 12  
    Gdyby jeszcze vtable lądowały w flash, to było by już całkiem ok :)
  • #12
    tmf
    Moderator of Microcontroller designs
    @BlueDraco A jakiż to masz overhead związany z alokacją dynamiczną? Raptem dwa bajty na chunk? Tak samo z VM - jeśli trzeba korzystać z metod wirtualnych to w C trzebaby to implementować na około za pomocą wskaźników. W obu przypadkach 2 bajty na VM.
    Dla przykładu - całe GUI obiektowe GUI (ze 40 różnych klas implementujących widgety) zajmuje mi jakieś 1600-2500 bajtów SRAM (ze względu na nieszczęsne przenoszenie VTABLES do SRAM) + kilkanaście bajtów na widget (pozycja i rozmiary, atrybuty). Może nie jest to coś na ATTiny, ale ATMega i XMEGA spokojnie udźwignie. Niedawno ktość pisał uszczypliwie, że celem wielu osób programujących MCU jest, aby jak najwięcej pamięci FLASH miało 0xFF. Ja dodam drugą część sentencji - "...i aby jak najwięcej RAM miało wartość 0x00" :)
  • #13
    BlueDraco
    MCUs specialist
    W C mamy implementację metod wirtualnych "na około" za pomocą wskaźników, a w C++ "po prostu" za pomocą takich samych samych wskaźników. Faktycznie, zysk jak jasny grom. A po co komu w ogóle te wirtulane metody w tak małej maszynie? ;)

    Szczerze, to kompletnie nie rozumiem, w jakim celu należy uprawiać alokację dynymiczną w komputerze, któremu ledwie starcza pamięci bez alokacji dynamicznej. Ja już tak mam, jestem staroścwiecki i oszczędny, więc się nie przejmuj... ;)
  • #14
    Freddie Chopin
    MCUs specialist
    Wypada się tylko zgodzić z tmf - niektórzy już zapewne obawiają się, że krowy przestaną dawać mleko a kury przestaną nieść jajka bo w okolicy użyty został C++ (;

    Można by się kiedyś pokusić o naszkicowanie krótkiej charakterystyki zatwardziałych przeciwników C++. Na pierwszym miejscu z pewnością będzie całkowite niezrozumienie zasady działania funkcji wirtualnych.
  • #15
    BlueDraco
    MCUs specialist
    Zacznij od wyjaśnienia, że wbrew błędnemu mniemaniu obiekt klasy z metodami wirtualnymi nie zawiera dodatkowego pola z identyfiakcą typu lub wskaźnikiem na wektor wskaźników na implementacje metod wirtualnych albo wprost pól wskaźników na te implementacje. ;)
    Potem wyjaśnij, że wywołanie metody wirtualnej nic nie kosztuje, bo dostęp przez łańcuszek wskaźników jest równie szybki jak zwykłe wywołanie funkcji o statycznym adresie.

    Freddie, ja rozumiem, że ktoś może lubć paradygmat obiektowy, ale niekoniecznie do wszystkiego się on nadaje, zwłaszcza, gdy bawimy się w takie rzeczy, jak metody wirtualne. To zawsze jest wolne i zasobożerne, tylko może ciut mniej to przeszkadza na wielogigahercowym i wielogigabajtowym PC.
  • #16
    Freddie Chopin
    MCUs specialist
    Mylisz się. Funkcje wirtualne rozwiązują konkretny problem, którym jest wywołanie jakiejś akcji zależnie od typu. Technicznie rzecz ujmując jest to odpowiednik instrukcji warunkowych. Funkcje wirtualne nie zastępują funkcji "zwykłych", tylko zastępują rozbudowane bloki warunkowe lub wskaźniki na funkcje. W takim ujęciu funkcje wirtualne są albo szybsze (bloki warunkowe) albo równie szybkie (wskaźniki na funkcje). Porównania wykonywane przez osoby nieznające C++ są typu "no powiedz mi jeszcze że ciężarówka zużywa mniej paliwa niż Fiat 125p, i może jeszcze łatwiej nią zaparkować"... Ciężarówka i Fiat 125p mają inne zastosowanie - tak samo jak "zwykłe" i wirtualne funkcje.

    Oto kawałek kodu, który można zastąpić funkcjami wirtualnymi:

    Code: cpp
    Log in, to see the code


    Nawet nie chce mi się liczyć w ilu miejscach tutaj można się pomylić... Nie mówię również nic o tym jak bardzo _NIE_skalowalne jest to rozwiązanie.

    Oto kod z tą samą funkcjonalnością, tylko z użyciem funkcji wirtualnych:

    Code: cpp
    Log in, to see the code


    Ilość miejsc w których można się pomylić - minimalna. Kod ze 2x krótszy. Lepszej skalowalności już się nie da osiągnąć.

    A nawet jeśli chcecie rozważać o tym jakie to "wolne" i "zasobożerne", to proszę bardzo.

    "Wolne" - na ARM normalna funkcja wywoływana jest powiedzmy w 3 takty zegara. Funkcja wirtualna zostanie wywołana w 3 takty + ze 3 takty na odczyt adresu funkcji do rejestru. No tragedia. Całe 125 nanosekund dłużej (przy 24MHz zegarze - bądźmy łaskawi dla tej dyskusji)! To tragedia, przecież tu każdy robi projekty które obciążają układ w 100,000000000%! Ciekawe czy blok instrukcji warunkowych jest szybszy...

    "Zasobożerne" - wskaźnik na vtable zajmuje tyle co jeden wskaźnik. No masakra! Rozwiązanie które zastępują funkcje wirtualne zajmuje owszem może mniej, bo 1 bajt, ale równie dobrze - gdyby np. klasy odziedziczone zawierały zmienną wymagającą wyrównania do 2, 4 czy 8 bajtów - może zająć tyle samo. Nie mówiąc już o tym co zadziała szybciej.

    Ja was doskonale rozumiem, ponieważ gdzieś tak do 3 roku studiów również myślałem, że programowanie obiektowe to bezsensowny pomysł który nie ma żadnych zalet. A potem w końcu to zrozumiałem...

    Jedne z NAJBARDZIEJ wymagających aplikacji tworzonych dziś - zarówno pod względem szybkości działania jak i ilości zużywanej pamięci - są tworzone praktycznie tylko w C++ i to z wykorzystaniem sporej ilości obiektowych "ficzerów". Mowa o grach komputerowych. Czyżby wszyscy programiści świata nie kumali, że gdyby tylko użyli czystego C to ich gry działałyby 3x szybciej i wymagały 2x mniej RAM?
  • #17
    tmf
    Moderator of Microcontroller designs
    @BlueDraco O jakim łańcuszku wywołań piszesz? Przecież wskaźnik wskazuje na obiekt, z jego VMT, która jest tworzona na etapie kompilacji. Jeśli masz wskaźnik na obiekt typu np. Primary, który ma VM, pokryte w obiekcie Secondary i wywołasz (Primary*)ptr->VM to wywoła się VM z Secondary, bo jej adres zostanie pobrany z VMT wskazywanego przez ptr. Nie ma czegoś w stylu - pobierze VM z Primary, sprawdź czy nie jest pokryta i ew. pobierz VM z Secondary...
    Druga sprawa - klasy bez metod wirtualnych nie mają VMT, a wywołanie zawartych w nich metod nie różni się od wołania funkcji w C - nawet jeśli to nie jest potrzebne kompilator nie daje im domyślnie argumentu this. Statyczne metody obiektów już w 100% odpowiadają funkcjom w C, bo nigdy nie dostają domyślnego argumentu this. Kolejna sprawa - obecnie gcc całkiem nieźle sobie radzi z porzucaniem metod wirtualnych, jeśli wirtualizacja nie jest potrzebna, w dodatku jest to optymalizacja mocno rozwijana w kompilatorze. Dużo wirtualizacji można ominąć korzystając z szablonów.
    A na koniec - IMHO źle wykorzystane metody wirtualne to takie samo zło, jak niepotrzebnie wywoływanie w C funkcji przez wskaźniki. Oczywiście, w C, w którym domyślnie jest to trudniejsze, niż dopisanie virtual, programiści ze wskaźników na funkcje rzadziej korzystają, stąd pozornie zło jest mniejsze.
    Osobiście używam C++ na AVR i nigdy z tego powodu nie odczułem żadnej przykrości. Kod jest tak samo efektywny jak kod w C, za to wiele elementów jest czytelniejsze. Co oczywiście nie znaczy, że każdy musi z C++ korzystać.
    Nie wiem też skąd twoje przekonanie, że alokacja dynamiczna jest bardziej pamięciożerna niż statyczna. IMHO wszystko zależy. Prosty przykład. Obsługa 1-wire. Nie wiesz ile na magistrali jest urządzeń. Odczytujesz ich ID i trzeba je gdzieś zapisać. Można zrobić tablicę i przyjąć, że masz max. np. 10 urządzeń. Ale wtedy jak masz >10 to resztę zgubisz, a jak <10 to część pamięci się marnuje. Robisz listę i aloację dynamiczną i wszystko elegancko działa. Inna sprawa to np. GUI. Przecież elementów interfejsu nie tworzysz statycznie. Przykładów jest więcej, oczywiście są też kontrprzykłady. Tylko IMHO wniosek z tego taki, ze alokację dynamiczną należy używać tam gdzie jest potrzebna.
  • #18
    BlueDraco
    MCUs specialist
    Funkcje wirtualne nie zastępują wskaźników na funkcje - to są wskaźniki na funkcje, czyli technika programowania niekiedy konieczna, której jednak, o ile to możliwe, należy unikać. Oczywiście, czasem jest to rozwiązanie najlepsze, jednak używanie go, gdy nie jest konieczne - to marnowanie zasobów i czasu procesora. Jeśli chodzi o zastosowanie i funkcjonalność - oczawiście zgadzam się z Tobą. Rzecz w tym, że delikwent nauczony podejścia obiektowego z metodami wirtualnymi używa ich często tam, gdzie jest to całkowicie zbędne.

    Ja, wbrew pozorom, uważam, że programowanie obiektowe ma całe mnóstwo zalet, ale niekoniecznie w zastosowaniu do maszyny z 8 KiB Flash i 1 KiB RAM. Sam często programuję "obiektowo" w C, jednak bez ekscesów stanowiących o urokach obiektowości dla entuzjastów tego paradygmatu, jak metody wirtualne i alokacja dynamiczna.

    Zasobożerne i czasożerne jak ... - wskaźnik na vtable, a w vtable wskaźniki na funkcje. Głupie wywołanie procedury wymaga dwóch stopni pośredniości. Ok, niekiedy to i tańsze niż duży switch, ale droższe niż if-then-else albo switch z trzema case. Jak często na mikrokontrolerze 8-bitowym masz metodę wirtualną z więcej niż trzema "wcieleniami"?

    Nie mówię tu o gigabajtowych aplikacjach na PC, a o programikach dla niedużych i wolnych mikrokontrolerów (na STM32F4 czy F7 z dużą pamięcią i 200 MHz to też jakiś sens ma).
  • #19
    kijas1
    Level 12  
    @tmf z tym omijaniem wirtualizacji z pomocą szablonów to chodzi o polimorfizm statyczny? Przydała by się jakaś literatura jak wydajnie korzystać z C++ na małe klocki(cos ala Scott Meyers), bo jednak język jest znacznie bardziej rozbudowany niż C i pewno dlatego tak małe zainteresowanie.
  • #20
    tmf
    Moderator of Microcontroller designs
    @BlueDraco O, dochodzimy do sedna :) Ja nigdzie nie twierdziłem, że wszystkie metody mają być wirtualne. Oczywiście należy z tego korzystać do celów do których to zostało stworzone. Poprzedzanie każdej metody słowem virtual, jest tak samo głupie jak poprzedzanie każdej definicji zmiennej modyfikatorem volatile :) Powiem szczerze, że cena, typu max dwa odczyty pamięci, vs. wygoda korzystania z VM powoduje, że korzystam z VM nawet jeśli mam tylko pojedyncze dziedziczenie. Tu znowu mógłbym zacytować... premature optimization is the root of all evil. Robię tak nawet na AVR, gdzie jak już pisano max głupie i bolesne jest kopiowanie VMT do SRAM.
    Nie mniej nie wiem dlaczego jak mam 8 kB FLASH to jest to gorsze rozwiązanie niż jeśli mam 128 kB FLASH. IMHO problem jest taki - mam potrzebną ilość pamięci, lub jej nie mam. Jeśli mam to w czym problem, a jeśli nie mam - to źle dobrałem procek. That's all folks...

    Dodano po 5 [minuty]:

    kijas1 wrote:
    @tmf z tym omijaniem wirtualizacji z pomocą szablonów to chodzi o polimorfizm statyczny? Przydała by się jakaś literatura jak wydajnie korzystać z C++ na małe klocki(cos ala Scott Meyers), bo jednak język jest znacznie bardziej rozbudowany niż C i pewno dlatego tak małe zainteresowanie.


    Tak, m.in. polimorfizm statyczny. Ale w gcc obecnie optymalizator potrafi analizować sposób wywołania VM i jeśli z kontekstu wynika, że funkcja VM jest wołana tak, że wiadomo z jakiego obiektu metoda zostanie użyta, to jest traktowana jak metoda niewirtualna. Szablony w tym pomagają.
    Co do przykładów, tutoriali itd na MCU - pewnie by się przydało, ale mitologia otaczająca C++ na MCU jest tak silna, że wiele osób woli bezpiecznie pisać w C, a jak się wspomni o C++ to właśnie, jak pisał @Freddie Chopin narażamy się na posądzenie, że przez nas krowy przestają mleko dawać, kury się nieść itd. A w najlepszym przypadku czeka nas powitanie - apage Satanas!
  • #21
    grko
    Level 33  
    Quote:

    Funkcje wirtualne nie zastępują wskaźników na funkcje - to są wskaźniki na funkcje, czyli technika programowania niekiedy konieczna, której jednak, o ile to możliwe, należy unikać


    Proceduralne programowanie również jest techniką programowania. Niesie ono również narzut na wykonanie programu. Jednak każdy z nas (chyba każdy:)) godzi się na ten narzut bo wie, że w zamian otrzymujemy czytelniejszy kod i istnieje możliwość ponownego użycia danej procedury w wielu miejscach. Podobnie jest z programowaniem obiektowym. Należy go używać tam gdzie to jest uzasadnione. Nikt tu nie nakłania do używania zaawansowanych technik OOP na ATiny z 2KB FLASH. Interfejsy w C++ czy tablica ze wskaźnikami do funkcji w C daje spore korzyści. Np:
    - jednolity dostęp do IO
    - wielopoziomowe drivery
    - oddzielenie warstwy sprzętowej oprogramowania od pozostałych funkcjonalności
    - ułatwia pisanie unit testów
    - i wiele innych


    Jeżeli Tylko zauważasz tylko to, że wywołanie kosztuje kilka instrukcji więcej i cała VFT zajmuje kilkanaście/kilkadziesiąt bajtów w pamięci FLASH to sorry, ale dalsza dyskusja z Tobą nie ma sensu.
  • #22
    tadzik85
    Level 38  
    tmf wrote:
    ale mitologia otaczająca C++ na MCU jest tak silna, że wiele osób woli bezpiecznie pisać w C,


    O jednej rzeczy należy również wspomnieć, a w całości się zgadzam. W większości przypadków nie mamy do czynienia z programistą jednostkowym a zespołem. Wyskoczenie więc jednostki z C++ jest daleko wówczas utrudnione.
  • #23
    alagner
    Level 26  
    @Tadzik85 no ok. Ale obostrzenia nie zawsze są, poza tym część kodu (np. ta odpowiedzialna za lot głowicy ;)) może być w C, reszta w C++ itp. Tu już wchodzi specyfika projektu/firmy...
  • #24
    tadzik85
    Level 38  
    Pewnie, że tak. Jak tylko twierdzę,że to nie zawsze kwestia zwykłego wyboru. Z resztą co już tu wiele razy zostało powiedziane jak umiesz dobrze w C a C++ jest zbyteczny,czasem się nie opłaca. Choć opór przed pójściem do przodu jest raczej negatywnym zjawiskiem. Przecież ten sam problem dotyczy RTOS czy wyboru architektury. Te kwestie to często osobiste preferencje. Dywagacje wyższości jednego nad drugim jest bez sensu. W każdym przypadku można zrobić i dobrze i źle. A argument czasu powstania jest mizerny. Bo niektórzy muszą się tej nowości nauczyć. Co owszem warto zrobić. Widać przecież większe zainteresowanie C++ i jego stosowanie, ale trochę poczekamy nim stanie się on norma na mikrokontrolerach. Bo to tka samo jak z wiecznie żywą 51.
  • #25
    alagner
    Level 26  
    Wg tego co Dan Saks ostatnio pokazywał to jest wręcz odwrotnie: jest ogromny SPADEK zainteresowania C++ w embedded. Ciekaw jestem za to na ile z powodu realnych problemów a na ile z mitologii, on sam obstawiał to drugie ale twardych dowodów nie pokazywał.
  • #26
    BlueDraco
    MCUs specialist
    No jasne, entuzjaści C++ nie uwierzą w żadne inne powody niż mitologia, uprzedzenia ew. spisek... ;)

    Osobiście nie mam żadnych uprzedzeń do C, C++, Javy, C# ani podobnych, ale uważam, że są miejsca, w których jedne języki pasują, a inne - nie. No może za wyjątkiem Basica/Bascoma i Arduino, które nie pasują nigdzie. ;)
  • #27
    Freddie Chopin
    MCUs specialist
    BlueDraco wrote:
    No jasne, entuzjaści C++ nie uwierzą w żadne inne powody niż mitologia, uprzedzenia ew. spisek... ;)

    Osobiście nie mam żadnych uprzedzeń do C, C++, Javy, C# ani podobnych, ale uważam, że są miejsca, w których jedne języki pasują, a inne - nie. No może za wyjątkiem Basica/Bascoma i Arduino, które nie pasują nigdzie. ;)

    Powiem Ci tak - od paru lat rozwijam kilka projektów (komercyjnych) napisanych (przeze mnie) w C++11, w których użyty jest RTOS napisany w C. Najpoważniejsze problemy w tych projektach zawsze związane są z błędem w RTOSie (napisanym w C), za to nigdy z kodem w C++...

    Są pewne rzeczy w C++ które DRASTYCZNIE upraszczają pisanie funkcji, tak że stają się one wielokrotnie krótsze i prostsze, a do tego niezawodne - mówię tu np. o RAII (przykładowo std::unique_ptr). Cokolwiek byś nie wykombinował, w C nie masz ani konstruktorów, ani destruktorów (rozszerzenia GCC się nie liczą) i nigdy ich mieć nie będziesz. Rozumiem że wg Ciebie lepszy jest program z milionem zagnieżdżonych ifów, ewentualnie z goto (często używanym w tym właśnie celu - jako cleanup po wystąpieniu błędu), dodatkowo często ograniczony do używania schematu single-entry-single-exit dla funkcji, zamiast z obiektami w których sprzątanie następuje w destruktorach? No chyba że destruktory i konstruktory są "ciężkie i wolne", podobnie jak funkcje wirtualne?

    Tak więc faktycznie - to musi być ograniczony horyzont miłośników C++, że nie potrafią zrozumieć przeciwników. Czy w ogóle ktoś podał jakiś rozsądny argument przeciwko C++, inny niż "bo jest strasznie wolny, produkuje ogromny kod i nikt go nie zna"? Może przykład podobnych projektów zrobionych w C i C++, tak żeby można było ocenić który jest lepszy. Np. chętnie zobaczę jak "prosty i czytelny" będzie framework do jakiegoś GUI napisany w czystym C. Miałem już "przyjemność" obcować z emWin i było to np. średnio przyjemne doznanie, ale tu chyba znów wychodzi mój ograniczony horyzont i bezkrytyczne uwielbienie dla C++...
  • #28
    grko
    Level 33  
    @Freddie Chopin
    Co powiesz na krytykę C++ no przez Linusa? Jakby nie było, gościu zna się na rzeczy i zrobił kilka projektów w C które podbiły świat (kernel, git). Jest sporo osób które krytykują konstruktywnie C++. Czy uważasz, że zastąpienie C przez C++ naprawi źle napisane programy? Czy uważasz, że w C nie może być dobrych programów/bibliotek? Osobiście nie krytykuję C++ jednak staram się zrozumieć argumenty obu stron.

    PS: nie polemizuję z ideą programowania obiektowego bo sam to stosuję tylko z samym językiem C++
  • #29
    BlueDraco
    MCUs specialist
    Freddie: wg. mnie do dużych i złożonych programów na dużych maszynach należy używać mądrzejszych języków - takich, jak Java czy C++. Do małych rzeczy należy dobrać narzędzie i styl programowania do oczekiwanej wydajności. Wiem, że można elegancko i błędoodpornie mrugać diodą w C++ bez narzutu wydajności, jednak użycie wyrafinowanych własności C++ w takich urządzeniach przez bezkrytycznych fanów obiektowości, polimorfizmu, dynamicznej alokacji itp. wodotrysków zwykle skutkuje rozrostem kodu wynikowego i gorszą wydajnością.

    Nie wyobrażam sobie pisania dużego GUI i ogólnie dużych programów w języku nieobiektowym. Na szczęście ja piszę tylko małe i średnie programy. ;)

    C++ nie jest lepszą i nowszą wersją C, o czym wielu fanów C++ nie wie lub zapomina. To dwa różne języki o dość podobnej składni. O C++ raczej trudno powiedzieć, że jest "asemblerem wysokiego poziomu", co dość dobrze oddaje charakterystykę C.
    No i wreszcie C++ jest błędogenny trochę mniej niż C, a zapewne znacznie bardziej niż Java czy C#.
  • #30
    Freddie Chopin
    MCUs specialist
    GrzegorzKostka wrote:
    Co powiesz na krytykę C++ no przez Linusa? Jakby nie było, gościu zna się na rzeczy i zrobił kilka projektów w C które podbiły świat (kernel, git).

    Nie no, nie przywołujmy tutaj tej bzdury (; Tyle mam do powiedzenia na ten temat - http://warp.povusers.org/OpenLetters/ResponseToTorvalds.html

    GrzegorzKostka wrote:
    Jest sporo osób które krytykują konstruktywnie C++.

    Zwykle ich argumenty są takie jak w tym wątku "strasznie wolne i zajmujące terabajty RAMu funkcje wirtualne" i tym podobne głupoty. Konstruktywnie nie da się tych nieszczęsnych funkcji wirtualnych skrytykować, ponieważ implementacja adekwatnego mechanizmu w C jest albo identyczna, albo gorsza (wolniejsza)...

    GrzegorzKostka wrote:
    Czy uważasz, że zastąpienie C przez C++ naprawi źle napisane programy?

    Samo zastąpienie nie, ale to dobry pierwszy krok <;

    GrzegorzKostka wrote:
    Czy uważasz, że w C nie może być dobrych programów/bibliotek?

    Nie <:

    Nie ma co roztrząsać czy w danym projekcie zmiana języka coś pogorszy czy polepszy. Za to pytanie czy zaczynając nowy projekt wybrać C czy C++ jest już jak najbardziej zasadne. Przykładowo - istnieje wiele bibliotek w C i wiele bibliotek w C++. W C++ można bezproblemowo używać bibliotek z C, odwrotna kombinacja jest zwykle niemożliwa. W C++ można zrobić absolutnie wszystko to co jest możliwe w C (i oczywiście więcej, ale nie ma obowiązku), odwrotnie oczywiście nie. W kompilatorze C++ zwykle można skompilować program w C (i nawet ma to sporo zalet, bo C++ jest bardziej "czuły" na błędy związane z typami zmiennych), odwrotny układ oczywiście nie jest możliwy.

    Moja opinia jest taka - identyczna funkcjonalność (niekoniecznie w formie identycznego kodu) w C i w C++ zajmie dokładnie tyle samo flasha, czasu, RAMu, ... . Bardziej rozbudowane programy, które mają więcej "ficzerów", zajmują więcej - niezależnie od tego czy są w C czy w C++. Niektóre bardzo wygodne i darmowe cechy czy techniki programowania (np. RAII, enkapsulacja danych i funkcji, ścisła kontrola typów, referencje, klasy, ...) są w C++ dostępne "wprost", za to w C są niewykonalne. Niektóre bardzo tanie i bardzo wygodne techniki programowania obiektowego (np. polimorfizm) są dostępne w C++ "wprost", za to w C wymagają nieziemskich ewolucji z potencjalnymi setkami miejsc w których można się pomylić.

    Wcześniej BlueDraco pytał jak często mamy więcej implementacji klasy bazowej niż "2 czy 3". Otworzyłem więc projekt nad którym dziś pracuję, znalazłem klasę interfejsową modelującą rejestr MODBUSa i Eclipse twierdzi że dziedziczy z niej (pośrednio lub bezpośrednio) 21 klas. Czy mój projekt by zyskał, gdybym funkcje wirtualne zmienił na kaskadę "if .. else if ... else if ..." albo gigantycznego switch-case'a?

    Jeśli piszesz projekt przy użyciu paradygmatu obiektowego, to pisząc go w C++ miałbyś prostszy, czytelniejszy i krótszy kod, a publiczne API i tak mogłoby być w C. Prędkość wykonywania i ilość używanej pamięci mogą się zmienić - np. kod w C++ będzie mniejszy i szybszy [;

    Ponieważ nie chcę zabrzmieć jak niektórzy forumowicze projektujący urządzenia produkowane w "milionach sztuk" i działające "100 lat na jednej baterii", to oszczędzę sobie opinii w jakich projektach widać zalety C++, a w jakich projektach można spokojnie ograniczyć się do C [;