poniedziałek, 5 maja 2014

Różnice między C++ a C ciąg dalszy

1) W C++ istnieje dokładniejsza kontrola typów wyliczeniowych. Jeżeli definiujemy np. "enum typ" to w języku C jest możliwy zapis "typ++" natomiast w C++ nie.
2) W języku C definicja zmiennych musi nastąpić na początku wywołania funkcji main.
3) W języku C++ nie można wywołać niezadeklarowanej uprzednio funkcji.
4) C dopuszcza przypisanie wskaźnikowi do void każdego innego typu. W C i C++ możliwa jest następująca instrukcja (przy założeniu, że "integer" jest zmienną typu int równą 10): "void * wskaznik_do_void = & integer". Natomiast "int * wskaznik_do_integer = wskaznik_do_void" dopuszczalne jest tylko w C.
5) C nie pozwala aby struktura nie zawierała danych składowych.
6) W C dozwolony jest zapis np. "const int zmienna" natomiast w C++ nie. Kompilator języka C przyjmuje taki zapis jako deklarację obszaru pamięci przydzielonego w jakimś innym miejscu programu. Jest to dopuszczalne z uwagi na to, iż w języku C stałe są domyślnie łączone zewnętrznie. W C++ domyślnie w stosunku do stałych jest stosowane łączenie wewnętrzne. W C++ stałe nie zawsze zajmują pamięć (w przeciwieństwie do C).
7) Język C wymagał podczas deklaracji zmiennych strukturalnych poprzedzanie nazwy struktury łańcuchem "struct". W C++ jest to zbędne.
8) Struktury języka C nie mogą posiadać funkcji składowych.

Przeczytaj część pierwszą.

Źródło: Eckel B., Thinking in C++. Edycja polska, Helion SA, 2002

C++11

W stosunku do poprzedniej wersji języka C++ wprowadzono m.in. następujące nowości:
  • typ long long,
  • typy char16_t i char32_t dla zmiennych bez znaku odpowiednio o szerokości 16 i 32 bitów (literały znakowe i napisowe dla typu char16_t są poprzedzone literą u natomiast dla char32_t są poprzedzone literą U),
  • obsługa dla wariantu kodowania znaków UTF-8 (literał poprzedzamy przedrostkiem "u8"),
  • łańcuch literalny, który sprawia, że znaki nie są interpretowane przez kompilator czyli można je wpisywać wprost w łańcuchu jak np. znaki specjalne łącznie ze znakami cudzysłowu (przed literałem dodajemy tutaj "R"),
  • inicjalizacja zmiennych: "int zmienna{1};" oraz instrukcja "int zmienna{}", która wstawia do zmiennej wartość 0, 
  • podobnie inicjalizacja tablic: "double tablica[2] {1.26e4, 1.11e-8};"  i wypełnianie wartościami zerowymi w postaci "double tablica[10] {};",
  • nadawanie wartości początkowych łańcuchom znaków: "char znaki[] = {"Znaki"};" lub "char znaki[] {"Znaki"};", a także "string signs = {"Znaki"};" lub "string signs {"Znaki"};",
  • podobnie sytuacja ma miejsce w przypadku struktur: "struct struktura {"Pole 1", 2, 3.0}" i dla inicjalizacji wartościami zerowymi "struct struktura {}",
  • przyrostki "LL" i "ULL" dla literałów odpowiednio typu long long i unsigned long,
  • dedukowanie typu zmiennej na podstawie typu wartości inicjalizującej gdzie np. instrukcja "auto zmienna = 0.0" określa zmienną jako double, a instrukcja "auto zmienna = 0" jako int,
  • klasa szablonowa array będąca skuteczną i bezpieczną alternatywą dla tablic wbudowanych o stałym rozmiarze,
  • pętla zakresowa for postaci "for (typ zmienna : tablica)" gdzie "typ" jest typem danych przechowywanych w "tablica", a "zmienna" przyjmuje wartość pierwszego elementu z danego zestawu danych (pętla przechodzi po wszystkich elementach tablicy),
  • referencja do r-wartości deklarowana jest np. jako "int && r_value = (2 * zmienna  + 20);" co niedozwolone jest dla zwykłej referencji,
  • wyrażenie "decltype" umożliwia określenie tego samego typu zmiennej, np. wyrażenie "decltype(zmienna_1) zmienna_2;" przypisuje "zmienna_2" typ "zmienna_1", a z kolei wyrażenie "decltype(zmienna_1 + zmienna_2) zmienna_3;" przypisuje "zmienna_3" typ wyrażenia ujętego w nawiasy,
  • opóźniona deklaracja typu zwracanego: "auto funkcja(typ zmienna_1, typ zmienna_2) -> double;" czy też "auto funkcja(typ zmienna_1, typ zmienna_2) -> decltype(zmienna_1 + zmienna_2);",
  • specyfikator thread_local wskazujący, że czas życia zmiennej jest równy czasowi życia wątku zawierającego zmienną (zmienna z tym przydomkiem jest dla wątku tym samym co zmienna statyczna dla całego programu),
  • inicjalizacja operatorem new: możliwe są np. takie instrukcje jak "int * tablica = new int[2] {1, 2};" czy też "double * wskaznik = new int {1};",
  • inicjalizacja obiektu klasy za pomocą listy: "klasa obiekt = {"Argument", 1, 2.0};",
  • wyliczenia z własnym zasięgiem: deklarując typ wyliczeniowy jako "enum nazwa_klasy nazwa_typu_wyliczeniowego {Element_1, Element_2}" możemy odwoływać się do elementów wyliczenia np. jako "nazwa_typu_wyliczeniowego nazwa_odniesienia = nazwa_typu_wyliczeniowego::Element_1" co pozwala na eliminację problemu kolizji nazw elementów wyliczeń poprzez nadanie im zasięgu klasy,
  • w C++11 domyślnym typem wewnętrznym wyliczenia jest int jednak można ręcznie określić pożądanym typ wewnętrzny za pomocą wyrażenia o postaci np. "enum nazwa_klasy : short nazwa_typu_wyliczeniowego {Element1, Element2}" (typ wewnętrzny musi być typem całkowitoliczbowym,
  • w C++98 słowo "explicit" nie działa z funkcjami konwersji w przeciwieństwie do C++11,
  • notacja "nullptr" określająca wskaźnik pusty,
  • inicjalizacja składowych klas: zapis "class klasa { int zmienna = 1; };" równoważny jest zapisowi "klasa::klasa() : zmienna(1) { }" jako liście inicjalizacyjnej,
  • szablon będący argumentem typu dla innego szablonu: w poprzedniej wersji C++ w obawie przed mylną interpretacją operatora przesunięcia bitowego należało pisać deklarację w postaci np. "szablon < klasa<int> > obiekt;" natomiast w C++ ten problem został już zażegnany,
  • szablonowe aliasy typów: zamiast stosowania deklaracji np. "typedef std::klasa<int, 1> aliast" możemy wykorzystać instrukcję "template<typename type> using alias = std::klasa<type, 1>" co sprawia, że pisząc np. "alias<double> obiekt" określamy, iż "obiekt" jest typu "std::klasa<double, 1>",
  • klasa kontenerowa forward_list, która implementuje zwyczajną listę jednokierunkową,
  • szablon klasy array,
  • nieuporządkowane kontenery asocjacyjne typu unordered_set, unordered_multiset, unordered_map i unordered_multimap,
  • szablon initializer_list, który pozwala na stosowanie składni listy inicjalizującej do inicjalizowania kontenerów STL listami wartości,
Źródło: Prata S., Język C++. Szkoła programowania, Wydanie VI, Helion SA, 2012