Przejdź do zawartości

C++/Różnice między C a C++

Z Wikibooks, biblioteki wolnych podręczników.


"Hello World" program w C i C++.

Komentarze

[edytuj]

W ANSI C (C4) nie jest dozwolone używanie komentarzy zaczynających się od // Zostały jednak dodane w standardzie C4

Stałe

[edytuj]

Stałe w C++ (obiekty zadeklarowane ze słowem const) są stałe w pełnym tego słowa znaczeniu. Np. stałe typów całkowitych mogą być stosowane tam, gdzie wymaga się stałych wyrażeń (tzn. jako etykiety case, jako rozmiar tablic itp.). W C już nie i tam do takich stałych trzeba stosować dyrektywę preprocesora #define. W C stała jest dokładnie tym samym co zmienna, z tym tylko zastrzeżeniem, że nie można jej jawnie modyfikować (ale można zmodyfikować zawartość wskaźnika do adresu stałej, czyli de facto zmodyfikować stałą).

Konsekwencją tego po części jest fakt, że globalnie deklarowane stałe w języku C mają to samo wiązanie (ang. linkage) co zmienne, czyli zewnętrzne. W języku C++ stałe mają domyślnie wiązanie lokalne i aby były one zewnętrzne (dzielone między jednostkami kompilacji), muszą być zadeklarowane razem z inicjalizacją i słowem extern.

Zmienne

[edytuj]
  • możliwość deklarowania zmiennych np. w instrukcji sterującej petli while
  • możliwość mieszania instrukcji i deklaracji zmiennych w jednym bloku kodu (w ANSI C zmienne muszą być deklarowane przed pierwszą instrukcją)

Wiązanie (ang. linkage) i obiekty niejawne

[edytuj]

W języku C wiązanie symboli z obiektami, które są przez nie oznaczane, czyli odwoływanie się w jednych jednostkach kompilacji do obiektów lub funkcji z innych jednostek kompilacji, jest opisane luźniejszymi regułami, niż w C++.

W języku C obowiązuje "słabe wiązanie" (ang. vague linkage), przy czym nie istnieją w tym języku żadne obiekty niejawne. To oznacza, że funkcja lub zmienna globalna o określonej nazwie może wystąpić dowolną ilość razy w całym zbiorze kompilacji (zbiorze jednostek kompilacji składających się na jeden plik wykonywalny lub bibliotekę dynamiczną). Podczas procesu wiązania wybierany jest w takim wypadku "pierwszy lepszy" ze wszystkich takich obiektów. Język C pozwala również na wielokrotne definicje zmiennej globalnej w tym samym pliku - definicje te, jak też definicje zmiennej o tej samej nazwie w innych jednostkach kompilacji będą się odnosić do dokładnie tej samej zmiennej. Właściwość ta pochodzi prawdopodobnie z czasów, gdy w C nie było słowa extern, więc deklarację zmiennej globalnej można było bez dodatkowych oznaczeń zamieścić w pliku nagłówkowym.

W języku C++ obowiązuje "silne wiązanie" (ang. strict linkage) dla obiektów jawnych, natomiast słabe dla obiektów niejawnych. Obiekty niejawne w C++ to są tablice metod wirtualnych tworzonych dla określonej klasy oraz funkcje inline. Silne wiązanie oznacza, że jeśli w zbiorze kompilacji zostaną znalezione dwa obiekty o tej samej nazwie, to linker zgłosi błąd i odmówi wiązania.

Typ stałej znakowej

[edytuj]

W języku C literał znakowy (stała znakowa), np. 'a' jest traktowana jako int, natomiast w C++ jest uważana za char.

Typ bool

[edytuj]

W C++ istnieje oficjalny typ bool i dwie stałe tego typu true i false, które służą do przechowywania wartości logicznych. Jest typem zwracanym operatorów porównawczych i relacji oraz typem przyjmowanym i zwracanym przez operatory && i ||. Ten typ musi mieć również wyrażenie podawane do if, while i drugiego wyrażenia w for.

Ze względu na wsteczną zgodność jednak pozostawiono domyślną konwersję typu bool na int, przy czym false i true są konwertowane odpowiednio na 0 i 1, natomiast w drugą stronę 0 konwertuje się na false i każda inna wartość całkowita na true.

Typy wskaźników

[edytuj]

W języku ANSI C dozwolone są niejawne konwersje pomiędzy różnymi typami wskaźnikowymi oraz pomiędzy typami wskaźnikowymi i typami całkowitymi. Co prawda wiele kompilatorów zgłasza ostrzeżenia przy próbach dokonania takiej konwersji bez jawnego rzutowania (za wyjątkiem konwersji, w których uczestniczy void*), nie jest ona jednak w ANSI C błędem.

W języku C++ niejawne konwersje pomiędzy wskaźnikami i referencjami do różnych typów są możliwe tylko w przypadku typów spokrewnionych, tzn. wskaźnik do klasy pochodnej może być niejawnie konwertowany na wskaźnik do klasy bazowej (w tym również niejako uważa się "typ void" za bazę dla wszystkich typów, zatem każdy wskaźnik na dane można niejawnie konwertować na void*). Wszelkie inne konwersje pomiędzy wskaźnikami do danych różnych typów oraz wskaźnikami i typami całkowitymi muszą być jawnie zrzutowane.

Warto zaznaczyć, że rzutowanie pomiędzy typami klasowymi, które są zhierarchizowane (np. rzutowanie wskaźnika do klasy bazowej na wskaźnik do klasy pochodnej) mogą się odbywać wyłącznie poprzez operator static_cast lub dynamic_cast. Użycie do tego celu rzutowania ogólnego "(typ)obiekt" lub reinterpret_cast może spowodować niezdefiniowane zachowanie.

Alternatywne słowa kluczowe

[edytuj]

W języku C++ dodano dodatkowe słowa kluczowe opisujące niektóre operatory. Operatory &&, || i ! możemy też zapisywać jako and, or i not. Istnieją także słowa dla operatorów bitowych &, | i ^: bitand, bitor i xor. Podobnie również dla operatorów połączonych z przypisaniem: and_eq , or_eq , xor_eq i not_eq . W obecnych czasach nie ma potrzeby ich używania ani pamiętania.

Biblioteka standardowa

[edytuj]

C++ używa innych nazw plików nagłówkowych dla biblioteki standardowej odziedziczonej z języka C - np. cstdio zamiast stdio.h. Zobacz też rozdział Przestrzenie nazw.

Funkcje

[edytuj]

W języku C pusta lista argumentów: funkcja() oznacza, że prototyp nie precyzuje argumentów przyjmowanych przez funkcję, natomiast deklaracja funkcja(void) oznacza, że funkcja nie przyjmuje argumentów.

W języku C++ puste nawiasy są tożsame z (void) - nie przyjmowanie argumentów, natomiast efekt taki, co puste nawiasy w C można uzyskać poprzez (...), czyli zmienną listę argumentów, ale bez określania argumentów początkowych (to z kolei nie jest dostępne w języku C).

Należy zwrócić szczególną uwagę, że jest to w istocie dość uciążliwe ułatwienie, że () jest tożsame z (void). W konsekwencji bowiem o ile wyrażenie (a) (gdzie 'a' jest jakąś zmienną) można odróżnić od nazwy typu w nawiasach, np. (int), to w przypadku () jest to nie do odróżnienia. Stąd mała niekonsekwencja w deklarowaniu obiektów wraz z argumentami konstruktora:

Klasa x( arg1, arg2 );

ale bez argumentów musi być deklarowane jako

Klasa x;

czyli bez nawiasów.

Należy też pamiętać, że odróżnianie argumentu w nawiasach od typu w nawiasach źle działa w przypadku obiektów tymczasowych:

Klasa1 obiekt( Klasa2() );

które, wbrew pozorom, nie deklaruje obiektu klasy Klasa1 z podaniem obiektu tymczasowego typu Klasa2 do konstruktora, lecz deklaruje funkcję o nazwie 'obiekt', która przyjmuje funkcję (bezparametrową, zwracającą Klasa2) i zwraca typ Klasa1. Rozwiązaniem jest dodanie nawiasów, więcej światła na ten problem rzucą poniższe przykłady:

Klasa1 o1( Klasa2 funkcja() ); // funkcja (przyjmująca funkcję)
Klasa1 o2( Klasa2 (int) );  // funkcja (przyjmująca funkcję przyjmującą jeden argument)
Klasa1 o3( Klasa2 (10) );   // obiekt (z podaniem tymczasowego obiektu, z podaniem wartości)
Klasa1 o4( (Klasa2()) );    // obiekt (z podaniem tymczasowego obiektu)

Dekorowanie (mangling) nazw funkcji

[edytuj]

W związku z przeciążaniem funkcji, każda funkcja w C++ ma unikalną identyfikację, niezależną od jej nazwy. Ten identyfikator służy również do rozpoznania odpowiedniej wersji funkcji na poziomie wiązania - nawet jeśli istnieje prototyp funkcji o określonej nazwie, ale z innymi parametrami, niż te, z którymi została ta funkcja zdefiniowana, to błąd przy próbie wywołania takiej funkcji zostanie wykryty na etapie wiązania (w przypadku języka C nie zostałby wykryty w ogóle).

To spowodowało niezgodność sposobu wiązania funkcji z językiem C. Żeby móc w C++ użyć funkcji zdefiniowanej w języku C, to jej prototyp musi być poprzedzony extern "C". Taka funkcja nie może wtedy podlegać przeciążaniu (tzn. może być wiele funkcji o takiej nazwie, ale tylko jedna z nich może być extern "C").

Struktury

[edytuj]

Jeśli mamy strukturę Struktura to w C zmienne definiujemy struct Struktura s1, s2;. W C++ możemy pominąć słowo kluczowe struct (i podobnie jest z union, enum i class). Dla zgodności z językiem C jednak nie jest zabronione ponowne użycie nazwy struktury w innym znaczeniu (np. funkcji, co ma miejsce w przypadku standardowej funkcji z języka C stat), tyle że jeśli się tak stanie, to wtedy nie można już pominąć słowa struct, jeśli się ma na myśli określony typ strukturalny.