Porównanie Javy i C++
Java i C++ to dwa wybitne języki programowania zorientowanego obiektowo . Według wielu wskaźników popularności języków te dwa języki zdominowały tworzenie zorientowanego obiektowo i wysokowydajnego oprogramowania przez większą część XXI wieku i często są bezpośrednio porównywane i kontrastowane. Java pojawiła się około 10 lat później, a jej składnia była oparta na C/C++ .
Cele projektowe
Różnice między językami programowania C++ i Java można przypisać ich dziedzictwu , ponieważ mają one różne cele projektowe.
C++ został zaprojektowany do programowania systemów i aplikacji ( tzw. programowania infrastrukturalnego), rozszerzając proceduralny język programowania C , który został zaprojektowany do wydajnego wykonywania. Do języka C dodano obsługę programowania obiektowego , obsługę wyjątków , zarządzanie zasobami w oparciu o czas życia ( RAII ), programowanie ogólne , metaprogramowanie szablonów oraz standardową bibliotekę C++ , która zawiera ogólne kontenery i algorytmy ( standardowa biblioteka szablonów lub STL), oraz wiele innych obiektów ogólnego przeznaczenia.
Java to zorientowany obiektowo język programowania ogólnego przeznaczenia, współbieżny, oparty na klasach, zaprojektowany w celu zminimalizowania zależności implementacyjnych. Opiera się na wirtualnej maszynie Java, aby być bezpiecznym i wysoce przenośnym . Jest dostarczany z obszerną biblioteką zaprojektowaną w celu zapewnienia pełnej abstrakcji podstawowej platformy. Java to statycznie typowany obiektowo język, który używa składni podobnej do C++ (ale z nią niekompatybilnej). Zawiera system dokumentacji o nazwie Javadoc .
Różne cele w rozwoju C++ i Java spowodowały różne zasady i kompromisy projektowe między tymi językami. Różnice są następujące:
C++ | Jawa |
---|---|
Rozszerza język C o programowanie obiektowe i programowanie ogólne . Kod C może być najbardziej poprawnie używany. | Silny wpływ składni C++/C. |
Kompatybilny z kodem źródłowym C , z wyjątkiem kilku przypadków narożnych . | Zapewnia Java Native Interface , a ostatnio Java Native Access jako sposób bezpośredniego wywołania kodu C/C++. |
Napisz raz, skompiluj gdziekolwiek (WOCA). | Napisz raz, biegnij gdziekolwiek /wszędzie (WORA/WORE). |
Umożliwia programowanie proceduralne , programowanie funkcjonalne , programowanie obiektowe , programowanie ogólne i metaprogramowanie szablonów . Preferuje mieszankę paradygmatów. | Umożliwia programowanie proceduralne , programowanie funkcyjne (od Javy 8) i programowanie ogólne (od Javy 5), ale zdecydowanie zachęca do paradygmatu programowania obiektowego . Obejmuje obsługę tworzenia języków skryptowych . |
Działa jako natywny wykonywalny kod maszynowy dla docelowego zestawu instrukcji . | Działa na maszynie wirtualnej . |
Zapewnia typy obiektów i nazwy typów. Umożliwia odbicie za pomocą informacji o typie czasu wykonywania (RTTI). | Jest refleksyjny , umożliwiając metaprogramowanie i dynamiczne generowanie kodu w czasie wykonywania. |
Ma wiele standardów kompatybilności binarnej (zwykle Microsoft (dla kompilatora MSVC) i Itanium/GNU (dla prawie wszystkich innych kompilatorów)). | Ma jeden standard kompatybilności binarnej, wieloplatformowy dla systemu operacyjnego i kompilatora. |
Opcjonalne automatyczne sprawdzanie granic (np. metoda at() w kontenerach wektorowych i łańcuchowych ). |
Wszystkie operacje muszą być sprawdzane przez wszystkie zgodne dystrybucje Javy. HotSpot może usunąć sprawdzanie granic. |
Natywna obsługa arytmetyki bez znaku . | Natywna arytmetyka bez znaku nie jest obsługiwana. Java 8 zmienia niektóre z nich, ale pewne aspekty są niejasne. |
Standardowe minimalne limity dla wszystkich typów liczbowych, ale rzeczywiste rozmiary są definiowane przez implementację. Standardowe typy są dostępne za pośrednictwem biblioteki standardowej <cstdint> . |
Ustandaryzowane limity i rozmiary wszystkich typów pierwotnych na wszystkich platformach. |
Wskaźniki, odwołania i przekazywanie wartości są obsługiwane dla wszystkich typów (prymitywnych lub zdefiniowanych przez użytkownika). | Wszystkie typy (typy pierwotne i typy referencyjne ) są zawsze przekazywane przez wartość. |
Zarządzanie pamięcią można wykonać ręcznie za pomocą funkcji new/delete , automatycznie według zakresu lub za pomocą inteligentnych wskaźników. Obsługuje deterministyczne niszczenie obiektów. Wyrzucanie elementów bezużytecznych ABI znormalizowane w C++ 11, chociaż kompilatory nie są wymagane do implementacji wyrzucania elementów bezużytecznych. |
Automatyczne zbieranie śmieci . Obsługuje niedeterministyczną metodę finalize(), której użycie nie jest zalecane. |
Zarządzanie zasobami może odbywać się ręcznie lub za pomocą automatycznego zarządzania zasobami na podstawie czasu życia ( RAII ). | Zarządzanie zasobami należy generalnie wykonywać ręcznie lub automatycznie za pomocą finalizatorów, chociaż generalnie jest to odradzane. Ma funkcję try-with-resources do automatycznego zarządzania zasobami w oparciu o zakres (wersja 7 i nowsze). Można to również zrobić za pomocą wewnętrznego interfejsu API |
Obsługuje klasy, struktury ( typy pasywnej struktury danych (PDS)) i związki i może przydzielać je na stercie lub stosie . | Klasy są przydzielane na stercie . Java SE 6 optymalizuje z analizą ucieczki , aby przydzielić niektóre obiekty na stosie . |
Zezwala na jawne zastępowanie typów i niektóre niejawne konwersje zawężające (dla zgodności z C). | Bezpieczeństwo typu sztywnego , z wyjątkiem konwersji poszerzających. |
Standardowa biblioteka C++ została zaprojektowana z myślą o ograniczonym zakresie i funkcjach, ale obejmuje obsługę języków, diagnostykę, narzędzia ogólne, ciągi znaków, ustawienia regionalne, kontenery, algorytmy, iteratory, obliczenia liczbowe, wejście/wyjście, generatory liczb losowych, analizę wyrażeń regularnych , wątki , cechy typu (do statycznej introspekcji typu) i standardowa biblioteka C. Biblioteka Boost oferuje więcej funkcji, w tym sieciowe wejścia/wyjścia. Istnieje bogata liczba bibliotek innych firm dla GUI i innych funkcji, takich jak: Adaptive Communication Environment (ACE), Crypto++ , różne biblioteki XMPP Instant Messaging (IM), OpenLDAP , Qt , gtkmm . |
Standardowa biblioteka rozwijała się z każdym wydaniem. W wersji 1.6 biblioteka obejmowała obsługę ustawień regionalnych, rejestrowania, kontenerów i iteratorów, algorytmów, programowania GUI (ale bez korzystania z systemowego GUI), grafiki, wielowątkowości, sieci, bezpieczeństwa platformy, introspekcji, dynamicznego ładowania klas, blokowania i nie -blokowanie wejść/wyjść. Zapewnia interfejsy lub klasy wsparcia dla XML , XSLT , MIDI , łączności z bazami danych, usług nazewnictwa (np. LDAP ), kryptografii, usług bezpieczeństwa (np. Kerberos ), usług drukowania i usług sieciowych. SWT oferował abstrakcję dla GUI specyficznych dla platformy, ale został zastąpiony przez JavaFX w najnowszych wydaniach; pozwalając na akcelerację grafiki i interfejsy użytkownika z motywem CSS. Chociaż nie obsługuje żadnego rodzaju obsługi „natywnego wyglądu platformy”. |
Przeciążenie operatora dla większości operatorów. Zachowanie znaczenia (semantyki) jest wysoce zalecane. | Operatorów nie można zastąpić. Język zastępuje + i += dla klasy String. |
Pojedyncze i wielokrotne dziedziczenie klas, w tym dziedziczenie wirtualne. | Obsługuje tylko pojedyncze dziedziczenie klas. |
Szablony czasu kompilacji. Pozwala na kompletne metaprogramowanie Turinga. | Generics są używane do osiągnięcia podstawowej parametryzacji typu, ale nie tłumaczą z kodu źródłowego na kod bajtowy ze względu na użycie kasowania typu przez kompilator. |
Wskaźniki funkcyjne, obiekty funkcyjne, wyrażenia lambda (w C++11 ) i interfejsy. | Odwołania do funkcji, obiekty funkcyjne i wyrażenia lambda zostały dodane w Javie 8 . Klasy (i interfejsy, które są klasami) mogą być również przekazywane jako referencje przez SomeClass.class i someObject.getClass() . |
Brak standardowego wbudowanego mechanizmu dokumentacji. Istnieje oprogramowanie innych firm (np. Doxygen ). | Obszerny standard dokumentacji Javadoc dla wszystkich klas i metod systemowych. |
const do definiowania niezmiennych zmiennych i funkcji składowych, które nie zmieniają obiektu. Const-ness jest propagowana jako środek do wymuszenia w czasie kompilacji poprawności kodu w odniesieniu do zmienności obiektów (patrz const-correctness ). |
final zapewnia wersję const , równoważną ze wskaźnikami typu* const dla obiektów i const dla typów pierwotnych. Niezmienność elementów obiektu uzyskana dzięki interfejsom tylko do odczytu i enkapsulacji obiektów. |
Obsługuje instrukcję goto . |
Obsługuje etykiety z pętlami i blokami instrukcji. goto jest zastrzeżonym słowem kluczowym, ale w specyfikacji Java jest oznaczone jako „nieużywane”.
|
Kod źródłowy można napisać tak, aby był wieloplatformowy (można go skompilować dla Windows , BSD , Linux , macOS , Solaris itp. bez modyfikacji) i napisać tak, aby korzystał z funkcji specyficznych dla platformy. Zwykle kompilowany do natywnego kodu maszynowego, musi zostać ponownie skompilowany dla każdej platformy docelowej. | Skompilowany do kodu bajtowego Java dla JVM . Kod bajtowy jest zależny od platformy Java, ale zazwyczaj jest niezależny od specyficznych funkcji systemu operacyjnego . |
Funkcje językowe
Składnia
-
Składnia Java ma gramatykę bezkontekstową , którą można analizować za pomocą prostego parsera LALR . Parsowanie C++ jest bardziej skomplikowane. Na przykład
Foo<1>(3);
jest sekwencją porównań, jeśli Foo jest zmienną, ale tworzy obiekt, jeśli Foo jest nazwą szablonu klasy. - C++ dopuszcza stałe na poziomie przestrzeni nazw, zmienne i funkcje. W Javie takie jednostki muszą należeć do określonego typu, a zatem muszą być zdefiniowane wewnątrz definicji typu, klasy lub interfejsu .
- W C++ obiekty są wartościami, podczas gdy w Javie nimi nie są. C++ domyślnie używa semantyki wartości , podczas gdy Java zawsze używa semantyki referencyjnej . Aby wybrać semantykę referencyjną w C++, można użyć wskaźnika lub referencji.
C++ | Jawa |
---|---|
0
0
class Foo { // Deklaruje klasę Foo int x = ; // Zmienna członka prywatnego. Zostanie // zainicjowany na 0, jeśli // konstruktor go nie ustawi. // (z C++11) public : Foo () : x ( ) // Konstruktor dla Foo; inicjalizuje {} // x na 0. Jeśli inicjator zostałby // pominięty, zmienna zostałaby // zainicjowana do wartości podanej // w deklaracji x. int bar ( int i ) { // Funkcja składowa bar() return 3 * i + x ; } };
|
class Foo { // Definiuje klasę Foo private int x ; // Zmienna składowa, zwykle deklarowana // jako prywatna w celu wymuszenia enkapsulacji // domyślnie inicjowana na 0 public Foo () { // Konstruktor dla Foo } // Konstruktor no-arg dostarczany domyślnie public int bar ( int i ) { / / Metoda członkowska bar() return 3 * i + x ; } }
|
Foo a ; // deklaruje, że a jest wartością obiektu Foo, // inicjowana przy użyciu konstruktora domyślnego. // Inny konstruktor może być użyty jako Foo a ( args ); // lub (C++11): Foo a { args };
|
Foo a = nowy Foo (); // deklaruje, że a jest referencją do nowego obiektu Foo // zainicjowanego przy użyciu domyślnego konstruktora // Jako Foo można użyć innego konstruktora a = new Foo ( args );
|
Foo b = a ; // kopiuje zawartość a do nowego obiektu Foo b; // alternatywna składnia to "Foo b(a)"
|
// Foo b = a; // zadeklarowałoby b jako referencję do obiektu wskazywanego przez a Foo b = a . klon (); // kopiuje zawartość obiektu wskazanego // do nowego obiektu Foo; // ustawia referencję b tak, aby wskazywała na ten nowy obiekt; // klasa Foo musi implementować interfejs Cloneable, // aby ten kod mógł się skompilować
|
za . x = 5 ; // modyfikuje obiekt a
|
za . x = 5 ; // modyfikuje obiekt, do którego odwołuje się a
|
std :: cout << b . x << std :: endl ; // wyprowadza 0, ponieważ b jest // jakimś obiektem innym niż a
|
systemu . na zewnątrz println ( b . x ); // zwraca 0, ponieważ b wskazuje // na jakiś obiekt inny niż a
|
Foo * c ; // deklaruje c jako wskaźnik do // obiektu Foo (początkowo // niezdefiniowany; może wskazywać gdziekolwiek)
|
Foo C ; // deklaruje, że c jest referencją do obiektu Foo // (początkowo null, jeśli c jest członkiem klasy; // konieczne jest zainicjowanie c przed użyciem // jeśli jest to zmienna lokalna)
|
c = nowy Foo ; // c jest ustawione na wartość adresu obiektu Foo utworzonego przez operatora new
|
c = nowy Foo (); // wiąże c, aby odwoływać się do nowego obiektu Foo
|
Foo & d = * c ; // wiąże d, aby odwoływać się do tego samego obiektu, na który wskazuje c
|
Foo d = do ; // wiąże d, aby odwoływał się do tego samego obiektu co c
|
do -> x = 5 ; // modyfikuje obiekt wskazywany przez c
|
do . x = 5 ; // modyfikuje obiekt, do którego odwołuje się c
|
re . słupek ( 5 ); // wywołuje Foo::bar() dla a c -> bar ( 5 ); // wywołuje Foo::bar() dla *c
|
re . słupek ( 5 ); // wywołuje Foo.bar() dla c . słupek ( 5 ); // wywołuje Foo.bar() dla c
|
std :: cout << d . x << std :: endl ; // zwraca 5, ponieważ d odnosi się // do tego samego obiektu, na który wskazuje c
|
systemu . na zewnątrz println ( d . x ); // zwraca 5, ponieważ d odwołuje się // do tego samego obiektu co c
|
- W C++ możliwe jest zadeklarowanie wskaźnika lub odwołania do obiektu const , aby uniemożliwić modyfikację go przez kod klienta. Funkcje i metody mogą również zagwarantować, że nie zmodyfikują obiektu wskazywanego przez wskaźnik za pomocą słowa kluczowego „const”. Wymusza to const-correctness .
- W Javie, w przeważającej części, poprawność const-correctness musi polegać na semantyce interfejsu klasy, tj. nie jest silnie wymuszana, z wyjątkiem publicznych elementów danych, które są oznaczone jako
final
.
C++ | Jawa |
---|---|
stała Foo * a ; // nie można zmodyfikować obiektu // wskazywanego przez a do a
|
końcowy Foo a ; // deklaracja "końcowej" referencji: // możliwa jest modyfikacja obiektu, // ale referencja będzie cały czas wskazywać // na pierwszy przypisany do niej obiekt
|
a = nowy Foo ();
|
a = nowy Foo (); // Tylko w konstruktorze
|
za -> x = 5 ; // NIELEGALNY
|
za . x = 5 ; // LEGALNE, składowe obiektu nadal mogą być modyfikowane // chyba że jawnie zadeklarowano final w klasie deklarującej
|
Foo * const b = nowy Foo (); // deklaracja wskaźnika "const" // możliwa jest modyfikacja obiektu, // ale wskaźnik będzie cały czas wskazywał // obiekt do niego przypisany
|
ostateczna Foo b = nowa Foo (); // deklaracja "końcowej" referencji
|
b = nowy Foo (); // NIELEGALNE, nie wolno go ponownie wiązać
|
b = nowy Foo (); // NIELEGALNE, nie wolno go ponownie wiązać
|
b -> x = 5 ; // LEGAL, obiekt nadal można modyfikować
|
b . x = 5 ; // LEGAL, obiekt nadal można modyfikować
|
- C++ obsługuje instrukcje
goto
, które mogą prowadzić do programowania kodu spaghetti . Z wyjątkiem instrukcji goto (która jest bardzo rzadko spotykana w prawdziwym kodzie i zdecydowanie odradzana), zarówno Java, jak i C++ mają w zasadzie te same przepływu sterowania , zaprojektowane w celu wymuszenia ustrukturyzowanego przepływu sterowania , i opierają się na instrukcjach break i continue w celu zapewnienia pewnych funkcje typugoto .
Niektórzy komentatorzy zwracają uwagę, że te oznaczone instrukcje sterowania przepływem łamią właściwość programowania strukturalnego pojedynczego punktu wyjścia. - C++ zapewnia funkcje niskiego poziomu, których w większości brakuje Javie (jedynym godnym uwagi wyjątkiem jest interfejs API
sun.misc.Unsafe
do bezpośredniego dostępu do pamięci i manipulowania nią). W C++ wskaźniki mogą być używane do manipulowania określonymi lokalizacjami pamięci, co jest zadaniem niezbędnym do pisania niskopoziomowych systemu operacyjnego . Podobnie, wiele kompilatorów C++ obsługuje wbudowany asembler . Kod języka asemblera można zaimportować do programu C i odwrotnie. Dzięki temu język C jest jeszcze szybszy. W Javie taki kod musi znajdować się w bibliotekach zewnętrznych i można uzyskać do niego dostęp tylko za pośrednictwem Java Native Interface , co wiąże się ze znacznym obciążeniem dla każdego wywołania.
Semantyka
- C++ dopuszcza wartości domyślne dla argumentów funkcji/metody. Jawa nie. Jednak przeciążanie metod może być użyte do uzyskania podobnych wyników w Javie, ale generuje nadmiarowy kod pośredniczący.
- Minimalnym kodem potrzebnym do skompilowania dla C++ jest funkcja, dla Javy to klasa.
- C++ umożliwia szereg niejawnych konwersji między typami natywnymi (w tym niektóre konwersje zawężające), a także umożliwia definiowanie niejawnych konwersji obejmujących typy zdefiniowane przez użytkownika. W Javie niejawne są tylko konwersje rozszerzające między typami natywnymi; inne konwersje wymagają jawnej składni rzutowania.
- W rezultacie, chociaż warunki pętli (
if
,while
i warunek wyjścia wfor
) zarówno w Javie, jak i C++ oczekują wyrażenia boolowskiego, kod taki jakif(a = 5)
spowoduje błąd kompilacji w Javie, ponieważ nie ma niejawna konwersja zawężająca z int na boolean, ale skompiluje się w C++. Jest to przydatne, jeśli kod był literówką ijeśli (a == 5)
było zamierzone. Jednak obecne kompilatory C++ zwykle generują ostrzeżenie, gdy takie przypisanie jest wykonywane w wyrażeniu warunkowym. Podobnie samodzielne instrukcje porównawcze, np.a==5;
, bez skutków ubocznych, zwykle prowadzą do ostrzeżenia.
- W rezultacie, chociaż warunki pętli (
- W przypadku przekazywania parametrów do funkcji, C++ obsługuje zarówno pass-by-reference , jak i pass-by-value . W Javie parametry pierwotne są zawsze przekazywane przez wartość. Typy klas, typy interfejsów i typy tablic są w Javie zbiorczo nazywane typami referencyjnymi i zawsze są również przekazywane przez wartość.
- Typy wbudowane Java mają określony rozmiar i zakres zdefiniowany przez specyfikację języka. W C++ minimalny zakres wartości jest zdefiniowany dla typów wbudowanych, ale dokładną reprezentację (liczbę bitów) można odwzorować na dowolne typy natywne preferowane na danej platformie.
- Na przykład znaki Java to 16-bitowe znaki Unicode , a łańcuchy składają się z sekwencji takich znaków. C++ oferuje zarówno wąskie, jak i szerokie znaki, ale rzeczywisty rozmiar każdego z nich zależy od platformy, podobnie jak używany zestaw znaków. Ciągi mogą być tworzone z dowolnego typu.
- Oznacza to również, że kompilatory C++ mogą automatycznie wybrać najbardziej wydajną reprezentację dla platformy docelowej (tj. 64-bitowe liczby całkowite dla platformy 64-bitowej), podczas gdy reprezentacja jest ustalona w Javie, co oznacza, że wartości mogą być przechowywane w -efektywny rozmiar lub musi uzupełnić pozostałe bity i dodać kod, aby emulować zachowanie o zmniejszonej szerokości.
- Zaokrąglanie i precyzja wartości zmiennoprzecinkowych i operacji w C++ jest definiowana przez implementację (chociaż tylko bardzo egzotyczne lub stare platformy odbiegają od standardu IEEE 754 ). Java udostępnia opcjonalny ścisły model zmiennoprzecinkowy ( strictfp ), który gwarantuje bardziej spójne wyniki na różnych platformach, choć kosztem prawdopodobnie wolniejszej wydajności w czasie wykonywania. Jednak Java nie jest ściśle zgodna ze standardem IEEE 754. Większość kompilatorów C++ będzie domyślnie zgodna z IEEE 754 (zwykle z wyłączeniem ścisłych zasad zaokrąglania i zgłasza wyjątki w wynikach NaN), ale zapewnia opcje zgodności o różnej surowości, aby umożliwić pewną optymalizację. Jeśli oznaczymy te opcje od najmniej zgodnej do najbardziej zgodnej jako szybka , spójna (Java's strictfp ), near-IEEE i strict-IEEE , możemy powiedzieć, że większość implementacji C++ domyślnie jest zbliżona do IEEE , z opcjami przełączania na szybkie lub ścisłe -IEEE , podczas gdy Java domyślnie jest szybka z opcją przełączenia na spójną .
- W C++ wskaźnikami można manipulować bezpośrednio jako wartościami adresu pamięci. Referencje Java są wskaźnikami do obiektów. Odwołania Java nie umożliwiają bezpośredniego dostępu do adresów pamięci ani manipulowania adresami pamięci za pomocą arytmetyki wskaźników. W C++ można konstruować wskaźniki do wskaźników, wskaźniki do int i double oraz wskaźniki do dowolnych miejsc w pamięci. Odwołania Java uzyskują dostęp tylko do obiektów, nigdy do prymitywów, innych odniesień lub dowolnych lokalizacji pamięci. W Javie pamięć może być odczytywana i zapisywana przez dowolne wartości przy użyciu interfejsu
sun.misc.Unsafe
, jednak jest to przestarzałe i niezalecane. - W C++ wskaźniki mogą wskazywać na funkcje lub funkcje składowe ( wskaźniki funkcji ). Równoważny mechanizm w Javie używa odwołań do obiektów lub interfejsów.
- Poprzez obiekty alokowane w stosie C++ obsługuje zarządzanie zasobami w określonym zakresie , technikę używaną do automatycznego zarządzania pamięcią i innymi zasobami systemowymi, która obsługuje deterministyczne niszczenie obiektów. Chociaż zarządzanie zasobami w określonym zakresie w C++ nie może być zagwarantowane (nawet obiekty z odpowiednimi destruktorami można przydzielać przy użyciu
nowych
i pozostawionych nieusuniętych), zapewnia to skuteczny sposób zarządzania zasobami. Zasobami współdzielonymi można zarządzać za pomocąshared_ptr
, wraz zesłabym_ptr
w celu przerwania cyklicznych odniesień. Java obsługuje automatyczne zarządzanie pamięcią za pomocą wyrzucania elementów bezużytecznych , które może zwalniać nieosiągalne obiekty nawet w obecności cyklicznych odniesień, ale inne zasoby systemowe (pliki, strumienie, okna, porty komunikacyjne, wątki itp.) nastąpi natychmiast po porzuceniu ostatniego odwołania do obiektu. - przeciążanie operatorów zdefiniowanych przez użytkownika . Przeciążanie operatorów umożliwia typom zdefiniowanym przez użytkownika obsługę operatorów (arytmetycznych, porównań itp.), takich jak typy pierwotne, za pośrednictwem implementacji zdefiniowanych przez użytkownika dla tych operatorów. Ogólnie zaleca się zachowanie semantyki operatorów. Java nie obsługuje żadnej formy przeciążania operatorów (chociaż jej biblioteka używa operatora dodawania do konkatenacji łańcuchów).
- Java obsługuje standardowy interfejs programowania aplikacji (API) do odbicia i dynamicznego ładowania dowolnego nowego kodu.
- C++ obsługuje statyczne i dynamiczne łączenie plików binarnych.
- Java ma generyczne , których głównym celem jest zapewnienie kontenerów bezpiecznych dla typów. C++ ma szablony czasu kompilacji , które zapewniają bardziej rozbudowaną obsługę programowania ogólnego i metaprogramowania. Java posiada adnotacje , które umożliwiają dodawanie dowolnych, niestandardowych metadanych do klas i metaprogramowanie za pomocą narzędzia do przetwarzania adnotacji .
- Zarówno Java, jak i C++ rozróżniają typy natywne (nazywane również typami podstawowymi lub wbudowanymi ) oraz typy zdefiniowane przez użytkownika (nazywane również typami złożonymi ). W Javie typy natywne mają tylko semantykę wartości, a typy złożone tylko semantykę referencyjną. W C++ wszystkie typy mają semantykę wartości, ale można utworzyć odwołanie do dowolnego typu, co pozwoli na manipulowanie obiektem za pomocą semantyki odniesienia.
- C++ obsługuje wielokrotne dziedziczenie dowolnych klas. W Javie klasa może wywodzić się tylko z jednej klasy, ale klasa może implementować wiele interfejsów (innymi słowy, obsługuje wielokrotne dziedziczenie typów, ale tylko jedno dziedziczenie implementacji).
- Java wyraźnie rozróżnia interfejsy i klasy. W C++ wielokrotne dziedziczenie i czysto wirtualne funkcje umożliwiają definiowanie klas, które działają prawie tak samo, jak interfejsy Java, z kilkoma drobnymi różnicami.
- Java obsługuje zarówno języki, jak i biblioteki standardowe dla wielowątkowości . Słowo
synchronized
w Javie zapewnia proste i bezpieczne blokady mutex do obsługi aplikacji wielowątkowych. Java zapewnia również solidne i złożone biblioteki do bardziej zaawansowanej synchronizacji wielowątkowej. Dopiero od C++ 11 istnieje zdefiniowany model pamięci dla wielowątkowości w C++ oraz obsługa bibliotek do tworzenia wątków i wielu prymitywów synchronizacji. Istnieje również wiele bibliotek innych firm do tego celu. - Funkcje składowe języka C++ można zadeklarować jako funkcje wirtualne , co oznacza, że metoda, która ma zostać wywołana, jest określana przez typ obiektu w czasie wykonywania (inaczej dynamiczne wysyłanie). Domyślnie metody w C++ nie są wirtualne (tj. opt-in virtual ). W Javie metody są domyślnie wirtualne, ale można je uczynić niewirtualnymi, używając słowa
final
(tj. opt-out virtual ). - Wyliczenia C++ są typami pierwotnymi i obsługują niejawną konwersję na typy całkowite (ale nie z typów całkowitych). Wyliczenia Java mogą być
public static enum{enumName1,enumName2}
i są używane jak klasy. Innym sposobem jest utworzenie innej klasy, która rozszerzajava.lang.Enum<E>
) i dlatego może definiować konstruktory, pola i metody jak każdą inną klasę. Począwszy od C++11 , C++ obsługuje również wyliczenia o silnym typie , które zapewniają większe bezpieczeństwo typu i jawną specyfikację typu magazynu. - Jednoargumentowe operatory „++” i „--”: w C++ „Operand powinien być modyfikowalną wartością . [pominięto] Wynikiem jest zaktualizowany operand; jest to wartość…”, ale w Javie „promocja liczb binarnych wspomniana powyżej może obejmować konwersję rozpakowywania i konwersję zestawu wartości. W razie potrzeby konwersja zestawu wartości {i/lub [...] konwersja boksu} jest stosowana do sumy przed jej zapisaniem w zmiennej.", czyli w Javie, po inicjalizacja "Integer i=2;", "++i;" zmienia referencję i, przypisując nowy obiekt, podczas gdy w C++ obiekt jest wciąż taki sam.
Zarządzanie zasobami
- Java oferuje automatyczne wyrzucanie elementów bezużytecznych , które w określonych okolicznościach można ominąć za pomocą specyfikacji Javy czasu rzeczywistego . Zarządzanie pamięcią w C++ odbywa się zwykle za pomocą konstruktorów, destruktorów i inteligentnych wskaźników . Standard C++ zezwala na wyrzucanie elementów bezużytecznych, ale go nie wymaga. Zbiórka śmieci jest rzadko stosowana w praktyce.
- C++ może przydzielać dowolne bloki pamięci. Java przydziela pamięć tylko poprzez tworzenie instancji obiektu. Dowolne bloki pamięci mogą być przydzielane w Javie jako tablica bajtów.
- Java i C++ używają różnych idiomów do zarządzania zasobami. Java opiera się głównie na wyrzucaniu elementów bezużytecznych, które mogą odzyskać pamięć, podczas gdy C++ opiera się głównie na Resource Acquisition Is Initialization (RAII). Znajduje to odzwierciedlenie w kilku różnicach między tymi dwoma językami:
- W C++ często przydziela się obiekty typów złożonych jako lokalne zmienne powiązane ze stosem, które są niszczone, gdy wykraczają poza zakres. W Javie typy złożone są zawsze przydzielane na stercie i zbierane przez moduł wyrzucania elementów bezużytecznych (z wyjątkiem maszyn wirtualnych, które używają analizy ucieczki do konwersji alokacji sterty na alokacje stosu).
- C++ ma destruktory, podczas gdy Java ma finalizatory . Oba są wywoływane przed cofnięciem obiektu, ale znacznie się różnią. Destruktor obiektu C++ musi być wywołany niejawnie (w przypadku zmiennych powiązanych ze stosem) lub jawnie, aby zwolnić obiekt. Destruktor wykonuje się synchronicznie tuż przed punktem w programie, w którym obiekt jest zwalniany. Synchroniczne, skoordynowane anulowanie inicjalizacji i zwalnianie alokacji w C++ spełniają zatem idiom RAII. W Javie dealokacja obiektów jest niejawnie obsługiwana przez moduł wyrzucania elementów bezużytecznych. Finalizator obiektu Java jest wywoływany asynchronicznie jakiś czas po ostatnim dostępie do obiektu i przed jego cofnięciem alokacji. Bardzo niewiele obiektów wymaga finalizatorów. Finalizator jest potrzebny tylko obiektom, które muszą gwarantować pewne oczyszczenie stanu obiektu przed cofnięciem alokacji, zwykle zwalniając zasoby zewnętrzne w stosunku do maszyny JVM. Ponadto finalizatory wiążą się z poważnymi ograniczeniami wydajności i znacznie wydłużają czas potrzebny na zwolnienie obiektów, dlatego ich użycie jest odradzane i przestarzałe w Javie 9.
- W przypadku RAII w C++ jeden typ zasobu jest zwykle opakowany w małą klasę, która przydziela zasób podczas budowy i zwalnia zasób po zniszczeniu oraz zapewnia dostęp do zasobu pomiędzy tymi punktami. Każda klasa, która zawiera tylko takie obiekty RAII, nie musi definiować destruktora, ponieważ destruktory obiektów RAII są wywoływane automatycznie po zniszczeniu obiektu tej klasy. W Javie bezpieczne synchroniczne zwalnianie zasobów można przeprowadzić deterministycznie za pomocą konstrukcji try/catch/finally.
- W C++ możliwe jest posiadanie wiszącego wskaźnika , nieaktualnego odniesienia do obiektu, który został już zwolniony. Próba użycia wiszącego wskaźnika zwykle kończy się niepowodzeniem programu. W Javie moduł wyrzucania elementów bezużytecznych nie zniszczy obiektu, do którego się odwołuje.
- W C++ możliwe jest posiadanie niezainicjowanych prymitywnych obiektów. Java wymusza domyślną inicjalizację.
- W C++ możliwe jest posiadanie przydzielonego obiektu, do którego nie ma prawidłowego odniesienia. Taki nieosiągalny obiekt nie może zostać zniszczony (zwolniony) i powoduje wyciek pamięci . W przeciwieństwie do tego, w Javie obiekt nie zostanie zwolniony przez moduł wyrzucania elementów bezużytecznych, dopóki nie stanie się nieosiągalny (przez program użytkownika). ( słabe referencje , które współpracują z modułem wyrzucania elementów bezużytecznych Java, aby umożliwić różne poziomy osiągalności). Odśmiecanie w Javie zapobiega wielu wyciekom pamięci, ale w pewnych okolicznościach wycieki są nadal możliwe.
Biblioteki
- C++ zapewnia międzyplatformowy dostęp do wielu funkcji zwykle dostępnych w bibliotekach specyficznych dla platformy. Bezpośredni dostęp z języka Java do natywnego systemu operacyjnego i funkcji sprzętowych wymaga użycia interfejsu Java Native .
Czas działania
C++ | Jawa |
---|---|
C++ jest kompilowany bezpośrednio do kodu maszynowego , który jest następnie wykonywany bezpośrednio przez jednostkę centralną . | Java jest kompilowana do kodu bajtowego , który następnie interpretuje wirtualna maszyna Java (JVM) w czasie wykonywania. Rzeczywiste implementacje Java dokonują kompilacji just-in-time do natywnego kodu maszynowego. Alternatywnie GNU Compiler for Java może kompilować bezpośrednio do kodu maszynowego. |
- Ze względu na nieograniczoną ekspresyjność, funkcje języka C++ niskiego poziomu (np. niesprawdzony dostęp do tablicy, surowe wskaźniki, punning typów ) nie mogą być niezawodnie sprawdzane w czasie kompilacji lub bez narzutu w czasie wykonywania. Powiązane błędy programowania mogą prowadzić do przepełnienia bufora niskiego poziomu i błędów segmentacji . Standardowa biblioteka szablonów zapewnia abstrakcje RAII wyższego poziomu (takie jak wektory, listy i mapy), które pomagają uniknąć takich błędów. W Javie błędy niskiego poziomu albo nie występują, albo są wykrywane przez wirtualną maszynę Javy (JVM) i zgłaszane do aplikacji w postaci wyjątku .
- Język Java wymaga określonego zachowania w przypadku dostępu do tablicy poza granicami, co generalnie wymaga sprawdzania granic dostępu do tablicy. Eliminuje to możliwe źródło niestabilności, ale zazwyczaj odbywa się to kosztem spowolnienia wykonywania. W niektórych przypadkach, zwłaszcza od czasu Java 7, analiza kompilatora może udowodnić, że sprawdzanie granic jest niepotrzebne i eliminuje je. C++ nie ma wymaganego zachowania w przypadku dostępu poza granicami macierzy natywnych, w związku z czym nie wymaga sprawdzania granic dla tablic natywnych. Standardowe kolekcje bibliotek C++, takie jak std::vector, oferują jednak opcjonalne sprawdzanie granic. Podsumowując, tablice Java są „zwykle bezpieczne; nieco ograniczone; często mają narzut”, podczas gdy tablice natywne C++ „mają opcjonalny narzut; są nieco nieograniczone; są prawdopodobnie niebezpieczne”.
Szablony a generyczne
Zarówno C++, jak i Java zapewniają narzędzia do programowania ogólnego , odpowiednio szablonów i typów ogólnych . Chociaż zostały stworzone, aby rozwiązywać podobne rodzaje problemów i mają podobną składnię, są zupełnie inne.
Szablony C++ Generyki Javy Klasy, funkcje, aliasy i zmienne mogą być szablonowane. Klasy i metody można uogólniać. Parametry mogą być zmienne, dowolnego typu, wartości całkowitej, literału znakowego lub szablonu klasy. Parametry mogą być dowolnym typem referencyjnym, włączając prymitywne typy w ramkach (tj. Integer, Boolean...). Oddzielne instancje klasy lub funkcji zostaną wygenerowane dla każdego zestawu parametrów podczas kompilacji. W przypadku szablonów klas zostaną utworzone wystąpienia tylko funkcji składowych, które są używane. Jedna wersja klasy lub funkcji jest kompilowana, działa dla wszystkich parametrów typu (poprzez wymazanie typu). Obiekty szablonu klasy tworzone z różnymi parametrami będą miały różne typy w czasie wykonywania (tj. różne instancje szablonów są odrębnymi klasami). Parametry typu są usuwane podczas kompilacji; obiekty klasy z różnymi parametrami typu są tego samego typu w czasie wykonywania. Powoduje to inny konstruktor. Ze względu na wymazywanie tego typu nie jest możliwe przeciążanie metod przy użyciu różnych instancji klasy generycznej. Implementacja szablonu klasy lub funkcji musi być widoczna w jednostce tłumaczeniowej, aby można było z niej skorzystać. Zwykle oznacza to posiadanie definicji w plikach nagłówkowych lub zawartych w pliku nagłówkowym. Począwszy od C++11 , możliwe jest użycie szablonów extern do oddzielnego kompilowania niektórych instancji. Do jej użycia wystarczy sygnatura klasy lub funkcji ze skompilowanego pliku klasy. Szablony mogą być wyspecjalizowane — można zapewnić oddzielną implementację dla określonego parametru szablonu. Leki generyczne nie mogą być wyspecjalizowane. Parametry szablonu mogą mieć domyślne argumenty . Przed C++11 było to dozwolone tylko dla klas szablonów, a nie funkcji. Parametry typu ogólnego nie mogą mieć argumentów domyślnych. Nieobsługiwane symbole wieloznaczne. Zamiast tego zwracane typy są często dostępne jako zagnieżdżone definicje typów . (Ponadto C++ 11 dodało słowo kluczowe auto
, które działa jako symbol wieloznaczny dla dowolnego typu, który można określić w czasie kompilacji).Symbole wieloznaczne obsługiwane jako parametr typu. Brak bezpośredniego wsparcia dla ograniczania parametrów typu, ale zapewnia to metaprogramowanie Obsługuje ograniczanie parametrów typu za pomocą „extends” i „super” odpowiednio dla górnych i dolnych granic; umożliwia wymuszanie relacji między parametrami typu. Umożliwia utworzenie wystąpienia obiektu z typem typu parametru. Wyklucza tworzenie wystąpienia obiektu z typem typu parametru (z wyjątkiem za pośrednictwem odbicia). Parametr typu szablonu klasy może być używany dla metod statycznych i zmiennych. Parametr typu klasy generycznej nie może być używany dla metod i zmiennych statycznych. Zmienne statyczne niewspółdzielone między klasami i funkcjami o różnych parametrach typu. Zmienne statyczne współdzielone między instancjami klas o różnych parametrach typu. Szablony klas i funkcji nie wymuszają relacji typu dla parametrów typu w swoich deklaracjach. Użycie nieprawidłowego parametru typu skutkuje niepowodzeniem kompilacji, często generując komunikat o błędzie w kodzie szablonu, a nie w kodzie użytkownika, który go wywołuje. Właściwe użycie szablonowych klas i funkcji jest uzależnione od odpowiedniej dokumentacji. Metaprogramowanie zapewnia te funkcje kosztem dodatkowego wysiłku. Pojawiła się propozycja rozwiązania tego problemu w C++11 , tzw. Concepts , planowana jest w kolejnym standardzie. Klasy i funkcje generyczne mogą wymuszać relacje typu dla parametrów typu w ich deklaracji. Użycie nieprawidłowego parametru typu powoduje błąd typu w kodzie, który go używa. Operacje na typach sparametryzowanych w kodzie ogólnym są dozwolone tylko w sposób, który może być gwarantowany przez deklarację. Skutkuje to większym bezpieczeństwem typu kosztem elastyczności. Szablony są kompletne według Turinga (patrz metaprogramowanie szablonów ). Generyki są również kompletne według Turinga
Różnorodny
- Java i C++ używają różnych środków do dzielenia kodu na wiele plików źródłowych. Java używa systemu pakietów, który określa nazwę pliku i ścieżkę dla wszystkich definicji programu. Jego kompilator importuje pliki wykonywalne klas . C++ używa kodu źródłowego pliku nagłówkowego do udostępniania deklaracji między plikami źródłowymi.
- Skompilowane pliki kodu Java są na ogół mniejsze niż pliki kodu w C++, ponieważ kod bajtowy Java jest zwykle bardziej zwarty niż natywny kod maszynowy , a programy Java nigdy nie są statycznie połączone.
- Kompilacja C++ zawiera dodatkową fazę wstępnego przetwarzania tekstu , podczas gdy Java nie. W związku z tym niektórzy użytkownicy dodają fazę przetwarzania wstępnego do procesu kompilacji, aby zapewnić lepszą obsługę kompilacji warunkowej.
- Operatory dzielenia i modułu w Javie są dobrze zdefiniowane, aby można je było obcinać do zera. C++ (przed C++11 ) nie określa, czy te operatory obcinają do zera, czy też „obcinają do -nieskończoności”. -3/2 zawsze będzie miało wartość -1 w Javie i C++11, ale C++03 może zwrócić -1 lub -2, w zależności od platformy. C99 definiuje dzielenie w taki sam sposób jak Java i C++11. Oba języki gwarantują (gdzie aib są typami całkowitymi), że
(a/b)*b + (a%b) == a
dla wszystkich aib (b!= 0). Wersja C++ 03 będzie czasami szybsza, ponieważ można wybrać dowolny tryb obcinania, który jest natywny dla procesora. - Rozmiary typów całkowitych są zdefiniowane w Javie (int jest 32-bitowy, long jest 64-bitowy), podczas gdy w C++ rozmiar liczb całkowitych i wskaźników jest zależny od kompilatora i interfejsu binarnego aplikacji (ABI) w ramach określonych ograniczeń. W ten sposób program Java będzie zachowywał się spójnie na różnych platformach, podczas gdy program C++ może wymagać dostosowania do niektórych platform, ale może działać szybciej z bardziej naturalnymi rozmiarami liczb całkowitych dla platformy lokalnej.
Przykład porównania C++ i Javy znajduje się w Wikibooks .
Wydajność
Oprócz uruchamiania skompilowanego programu Java, komputery z aplikacjami Java zazwyczaj muszą również uruchamiać wirtualną maszynę Java (JVM), podczas gdy skompilowane programy C++ mogą być uruchamiane bez aplikacji zewnętrznych. Wczesne wersje Javy były znacznie lepsze od języków kompilowanych statycznie, takich jak C++. Dzieje się tak, ponieważ instrukcje programowe tych dwóch blisko spokrewnionych języków mogą kompilować się do kilku instrukcji maszynowych w C++, podczas kompilacji do kilku kodów bajtowych obejmujących kilka instrukcji maszynowych, z których każda jest interpretowana przez JVM. Na przykład:
Instrukcja Java/C++ | Kod wygenerowany w C++ (x86) | Kod bajtowy generowany przez Javę |
---|---|---|
wektor[i]++;
|
mov edx ,[ ebp + 4h ] mov eax ,[ ebp + 1Ch ] inc dword ptr [ edx + eax * 4 ]
|
aload_1 iload_2 dup2 iaload iconst_1 iadd iastore |
Ponieważ optymalizacja wydajności jest bardzo złożonym zagadnieniem, bardzo trudno jest ogólnie określić ilościowo różnicę w wydajności między C++ a Javą, a większość testów porównawczych jest niewiarygodna i stronnicza. Biorąc pod uwagę bardzo różny charakter języków, trudno jest również określić ostateczne różnice jakościowe. Krótko mówiąc, optymalizacja w Javie ma nieodłączną nieefektywność i twarde ograniczenia, biorąc pod uwagę, że w dużej mierze opiera się ona na elastycznych abstrakcjach wysokiego poziomu, jednak użycie potężnego kompilatora JIT (jak w nowoczesnych implementacjach JVM) może złagodzić niektóre problemy. W każdym razie, jeśli nieefektywność Javy jest zbyt duża, skompilowany kod C lub C++ można wywołać z Javy za pośrednictwem JNI.
Niektóre nieefektywności nieodłącznie związane z językiem Java obejmują głównie:
- Wszystkie obiekty są przydzielane na stercie. Chociaż alokacja jest niezwykle szybka w nowoczesnych maszynach JVM przy użyciu „alokacji uderzeń”, która działa podobnie do alokacji stosu, wywołanie modułu wyrzucania elementów bezużytecznych może nadal negatywnie wpływać na wydajność. Nowoczesne kompilatory JIT do pewnego stopnia łagodzą ten problem dzięki analizie ucieczki lub wykrywaniu ucieczki w celu przydzielenia niektórych obiektów na stosie, od Oracle JDK 6.
- Projekty o krytycznym znaczeniu dla wydajności, takie jak wydajne systemy baz danych i biblioteki komunikatów, musiały korzystać z wewnętrznych, nieoficjalnych interfejsów API, takich jak
sun.misc.Unsafe
, aby uzyskać dostęp do ręcznego zarządzania zasobami i móc wykonywać alokację stosu; skutecznie manipulować pseudowskaźnikami. - Wiele wymaganych rzutów w czasie wykonywania, nawet przy użyciu standardowych kontenerów, powoduje spadek wydajności. Jednak większość z tych rzutowań jest statycznie eliminowana przez kompilator JIT.
- Gwarancje bezpieczeństwa wiążą się z kosztem czasu pracy. Na przykład kompilator musi umieścić w kodzie odpowiednie kontrole zakresu. Ochrona każdego dostępu do tablicy za pomocą sprawdzania zakresu nie jest wydajna, więc większość kompilatorów JIT będzie próbowała je wyeliminować statycznie lub przenosząc je z wewnętrznych pętli (chociaż większość natywnych kompilatorów dla C++ zrobi to samo, gdy opcjonalnie używane są kontrole zakresu).
- Brak dostępu do szczegółów niskiego poziomu uniemożliwia programiście ulepszenie programu tam, gdzie kompilator nie jest w stanie tego zrobić.
- Obowiązkowe użycie semantyki referencyjnej dla wszystkich typów zdefiniowanych przez użytkownika w Javie może wprowadzić duże ilości zbędnych pośrednich pamięci (lub skoków) (chyba że zostanie to pominięte przez kompilator JIT), co może prowadzić do częstych chybień pamięci podręcznej (inaczej przerzucania pamięci podręcznej ) . Ponadto optymalizacja pamięci podręcznej, zwykle za pomocą struktur danych i algorytmów uwzględniających pamięć podręczną lub nieświadomych pamięci podręcznej , może często prowadzić do poprawy wydajności o rząd wielkości, a także uniknąć degeneracji złożoności czasowej, która jest charakterystyczna dla wielu algorytmów pesymizujących pamięć podręczną i jest dlatego jedna z najważniejszych form optymalizacji; semantyka odniesienia, zgodnie z wymogami Javy, sprawia, że takie optymalizacje są niemożliwe do zrealizowania w praktyce (ani przez programistę, ani przez kompilator JIT).
- Wyrzucanie elementów bezużytecznych , ponieważ ta forma automatycznego zarządzania pamięcią wprowadza obciążenie pamięci.
Istnieje jednak wiele korzyści związanych z projektowaniem Javy, niektóre z nich zrealizowano, a niektóre tylko teoretyzowano:
- Wyrzucanie elementów bezużytecznych Java może mieć lepszą spójność pamięci podręcznej niż zwykłe użycie malloc / new do alokacji pamięci. Niemniej jednak istnieją argumenty [ słowa łasicy ] , że oba alokatory w równym stopniu fragmentują stertę i żaden z nich nie wykazuje lepszej lokalizacji pamięci podręcznej. Jednak w C++ alokacja pojedynczych obiektów na stercie jest rzadkością, a duże ilości pojedynczych obiektów są zwykle alokowane w blokach za pośrednictwem kontenera STL i/lub alokatora małych obiektów.
- Kompilacja w czasie wykonywania może potencjalnie wykorzystywać informacje o platformie, na której wykonywany jest kod, w celu skuteczniejszego ulepszania kodu. Jednak większość najnowocześniejszych kompilatorów natywnych (C, C++ itp.) generuje wiele ścieżek kodu, aby wykorzystać pełne możliwości obliczeniowe danego systemu. Można również wysunąć argument odwrotny, że natywne kompilatory mogą lepiej wykorzystywać optymalizację i zestawy instrukcji specyficzne dla architektury niż wieloplatformowe dystrybucje JVM.
- Kompilacja w czasie wykonywania pozwala na bardziej agresywne wstawianie funkcji wirtualnych niż jest to możliwe w przypadku kompilatora statycznego, ponieważ kompilator JIT ma więcej informacji o wszystkich możliwych celach wywołań wirtualnych, nawet jeśli znajdują się one w różnych dynamicznie ładowanych modułach. Obecnie dostępne implementacje JVM nie mają problemu z wstawianiem większości wywołań monomorficznych, głównie monomorficznych i dimorficznych, a trwają badania nad wstawianiem również wywołań megamorficznych, dzięki najnowszym ulepszeniom dynamicznym invoke dodanym w Javie 7. Wstawianie może pozwolić na dalsze optymalizacje, takie jak wektoryzacja pętli lub rozwijanie pętli , co skutkuje ogromnym ogólnym wzrostem wydajności.
- W Javie synchronizacja wątków jest wbudowana w język, więc kompilator JIT może potencjalnie, poprzez analizę ucieczki, uniknąć blokad, znacznie poprawić wydajność naiwnego kodu wielowątkowego.
Ponadto w języku C++ występują pewne problemy z wydajnością:
- Zezwolenie wskaźnikom na wskazywanie dowolnego adresu może utrudnić optymalizację ze względu na możliwość tworzenia aliasów wskaźników .
- Ponieważ kod generowany z różnych instancji tego samego szablonu klasy w C++ nie jest współdzielony (jak w przypadku generycznych wymazywanych typów w Javie), nadmierne używanie szablonów może prowadzić do znacznego zwiększenia rozmiaru kodu wykonywalnego (rozdęcie kodu ) . Ponieważ jednak szablony funkcji są agresywnie wstawiane, czasami mogą zmniejszyć rozmiar kodu, ale co ważniejsze, pozwalają na bardziej agresywną analizę statyczną i optymalizację kodu przez kompilator, co częściej czyni je bardziej wydajnymi niż kod bez szablonów. W przeciwieństwie do tego, kody generyczne Java są z konieczności mniej wydajne niż kod niezgeneralizowany.
- Ponieważ w tradycyjnym kompilatorze C++ łączenie dynamiczne jest wykonywane po wygenerowaniu i optymalizacji kodu w C++, wywołania funkcji obejmujące różne moduły dynamiczne nie mogą być wstawiane. Jednak nowoczesne kompilatory C++, takie jak MSVC i Clang + LLVM, oferują opcje generowania kodu w czasie łącza, które umożliwiają kompilację modułów do formatów pośrednich, co umożliwia wstawianie na końcowym etapie łączenia.
Oficjalny standard i odniesienie do języka
Specyfikacja języka
Język C++ jest zdefiniowany przez normę ISO /IEC 14882 , która jest publikowana przez komitet ISO/IEC JTC1/SC22/WG21 . Dostępna jest również najnowsza, poststandaryzacyjna wersja robocza C++ 17 .
Język C++ ewoluuje za pośrednictwem otwartego komitetu sterującego zwanego Komitetem ds. Standardów C++. W skład komitetu wchodzą twórca C++ Bjarne Stroustrup , przewodniczący Herb Sutter i inne wybitne osobistości, w tym wielu przedstawicieli przemysłu i grup użytkowników (tj. interesariuszy). Będąc komitetem otwartym, każdy może dołączyć, uczestniczyć i zgłaszać propozycje dotyczące nadchodzących wydań norm i specyfikacji technicznych. Komitet ma obecnie na celu wydawanie nowego standardu co kilka lat, chociaż w przeszłości procesy ścisłego przeglądu i dyskusje oznaczały dłuższe opóźnienia między publikacją nowych standardów (1998, 2003 i 2011).
Język Java jest zdefiniowany przez Specyfikację języka Java , książkę opublikowaną przez firmę Oracle.
Język Java nieustannie ewoluuje w ramach procesu zwanego Java Community Process , a światowa społeczność programistów jest reprezentowana przez grupę ludzi i organizacji — członków społeczności Java — która jest aktywnie zaangażowana w ulepszanie języka poprzez wysyłanie publicznych próśb — żądania specyfikacji Java — które muszą przejść formalną i publiczną weryfikację, zanim zostaną zintegrowane z językiem.
Brak solidnego standardu dla języka Java i nieco bardziej niestabilny charakter jego specyfikacji są stałym źródłem krytyki ze strony interesariuszy, którzy chcą większej stabilności i konserwatyzmu w dodawaniu nowych funkcji językowych i bibliotek. W przeciwieństwie do tego, komitet C++ jest również stale krytykowany z przeciwnego powodu, tj. ze względu na zbyt surowe i konserwatywne podejście oraz zbyt długie wydawanie nowych wersji.
Znaki towarowe
„C++” nie jest znakiem towarowym żadnej firmy ani organizacji i nie jest własnością żadnej osoby. „Java” jest znakiem towarowym firmy Oracle Corporation .
Linki zewnętrzne
- Różnica między C++ a Javą
- Obiektowe zarządzanie pamięcią: Java a C++
- Rozdział 2: How Java Differs from C , rozdział z Java in a Nutshell autorstwa Davida Flanagana
- Porównanie zarządzania zasobami Java i C++ — obszerny artykuł z przykładami
- Wydajność Java a C... znowu... - Dogłębne omówienie różnic w wydajności między Javą a C/C++
- Hyperpoly — porównanie Javy i C++