Witam
Zapadła męska decyzja i postanowiłem się zaprzyjaźnić trochę z "embedded" C++... Tzn. na razie to będzie (długo będzie...) raczej taki C-plus-minus / "C z wstawkami ++"
Do rzeczy - odświeżyłem sobie podstawy z kilku "prastarych" ksiąg, przeczytałem poradniki na forum i troszkę wygrzebałem z Internetów. W efekcie powstała lista kilku drobnych pytań i wątpliwości. Z góry dziękuję za wszelką pomoc, podpowiedź, wskazanie źródeł wiedzy, etc...
Primo - byłbym wdzięczny za wskazanie wszelkich wartościowych materiałów na temat C++ (szczególnie w kontekście łembeded) - poziom zdecydowanie zielono-początkowy
W Internecie jest sporo materiałów, ale nie chciałbym sobie zrobić "krzywdy" na starcie jakimś poradnikiem "wątpliwej jakości" - a nie mnie oceniać ich jakość. Mile widziane również wskazanie porządnych kodów do "samodzielnej analizy".
Secundo - pytania "konkretne":1. Jak wygląda rozmieszczenie składników klasy w pamięci? W C sprawa była prosta - składniki struktury lądowały w pamięci po kolei, ew. dochodził jakiś wypełniacz. A co z klasami? Co jeśli składniki prywatne i publiczne będą pomieszane, np:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Kompilator ma prawo zmienić ich kolejność w pamięci? Jak wygląda sprawa z wyrównaniem danych - można "upakować" klasę?
2. W C zmienne globalne/statyczne są zerowane -> czy w C++ tworząc globalny obiekt jakiejś klasy, jej składniki też zostaną wyzerowane? Trochę się pobawiłem i z moich obserwacji wynika że tak, ale nie wiem czy to "zasada" czy akurat "tak mi się trafia". Mój "kod testowy" (testuję w GCC
online):
język cpp
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Przykładowy wynik powyższego kodu:
język bash
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Za każdym razem składniki obiektu globalnego mam wyzerowane. Konstruktor tego nie robi, stąd mój powyższy wniosek - czy słuszny?
3. Jak rozumieć taki zapis:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Jak w C++ traktowany jest obiekt (jakiejś klasy) z atrybutem
volatile? Czy
volatile przenosi się na składniki obiektu tej klasy?
4. Jeżeli tworzę nowy obiekt za pomocą
new to najpierw jest wywoływany
operator new, a potem
konstruktor - zgadza się? A co jeśli przydział pamięci się nie powiedzie - tzn. dokładniej - czy jestem w stanie (tworząc operator new dla swojej klasy) zapobiec wywołaniu konstruktora jeśli nie mogę przydzielić pamięci dla nowego obiektu? (wyjątki schowane na dnie szafy i niech tam pozostaną)
5. Wątpliwość dotyczy szablonów funkcji i specjalizacji dla konkretnego przypadku. Poniżej kod testowy - jest szablon funkcji i dwie wersje specjalne (dla int i float). Obie wersje działają tak jak bym chciał (jeśli chodzi o efekty na ekranie), kompilator nie krzyczy, ale przypuszczam, że to jednak nie jest "to samo". Kod testowy:
język cpp
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
6. Można jakoś zabezpieczyć klasę przed utworzeniem kilku obiektów? (np. ten nieszczęsny jeden ADC w Atmedze). W "Internetach" pojawia się hasło "singleton", ale nie wiem czy to dobry trop?
7. Co łopatologicznie znaczy
constexpr? Tu i tak znalazłem opis, że jest to "wyrażenie stałe na etapie kompilacji"... no dobra, ale jak kompilator traktuje ten "constexpr" - jako "zapewniam się kochany kompilatorze, że możesz to policzyć jeśli się postarasz" czy jako "prawdopodobnie możesz to policzyć" czy "jeśli możesz to policzyć to policz"?
Spłodziłem np. taki piękny kod:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Co z tym nieszczęsnym
constexpr - kompilator tego nie policzy przy pracy (no bo jak), nie powinien więc delikatnie zasugerować, że "miało być stałe przy kompilacji a tu lipa"?
8. Przekazuję zmienną ulotną (
a) do funkcji przyjmującej zwykły int bez jawnego rzutowania - czemu kompilator na to pozwala, przecież obiecywali ścisłą kontrolę typów
język cpp
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Tercio - pytania mniej konkretne... :10. Jak to jest z tym narzutem dla konkretnych ficzerów z C++. Broń Boże nie chcę powielać baśni ludowych! Chodzi mi raczej o prosty przepis dla początkujących. Taki punkt startowy:
- te funkcje stosuj do woli
- przy tych pomyśl / doczytaj / poczekaj / zastanów się bo...
- te są bee i ani-misie-wasz!
Empirycznie sprawdziłem, że wszystkie moje programy napisane w "Cy", które skompilowały się w "Cy-py-py" były mniejsze od oryginałów w "Cy" - głównie za sprawę mądrzej dobranych rozkazów _call/_jmp.
Wydaje mi się, że elementy takie jak: szablony, przeładowanie nazw, argumenty domyślne, klasy - przy odrobinie ostrożności wprowadzają praktycznie pomijalny narzut (jeśli w ogóle jakiś wprowadzą).
Co jest w następnym kroku? Chyba dziedziczenie/funkcje wirtualne - próbowałem przebrnąć przez mądre opisy o
vtable'ach ale mnie to pokonało... Można jakoś pi*drzwi określić jakie konstrukcje jaki narzut powodują?
Z tego co się naczytałem to wyjątków raczej należy unikać. Są inne ficzery paskudne z definicji
?
11. Jak się przyzwyczaić do referencji, tzn. obawiam się, że analizując kod będę miał podejście "typu C". Tj. widząc wywołanie funkcji z argumentem
bez "&" podświadomie zakoduję, że - ona nie modyfikuje argumentu bo pracuje na kopii. Są jakieś sprawdzone sposoby jak się przestawić i nie wpadać w taką pułapkę?
Finito tymczasowo
Pozdrawiam Wojtek