C++와 Java에 대한 심층 분석

게시 됨: 2022-07-22

수많은 기사에서 C++와 Java의 기술적 기능을 비교하지만, 고려해야 할 가장 중요한 차이점은 무엇입니까? 예를 들어, Java가 다중 상속을 지원하지 않고 C++가 지원한다는 비교가 표시되면 이는 무엇을 의미합니까? 그리고 좋은 일입니까? 어떤 사람들은 이것이 Java의 장점이라고 주장하고 다른 사람들은 그것을 문제라고 선언합니다.

개발자가 C++, Java 또는 다른 언어를 모두 선택해야 하는 상황과 더 중요한 것은 결정이 중요한 이유 를 살펴보겠습니다.

기본 검토: 언어 빌드 및 생태계

C++는 TypeScript가 JavaScript로 컴파일하는 방식과 유사하게 C 컴파일러의 프런트 엔드로 1985년에 출시되었습니다. 최신 C++ 컴파일러는 일반적으로 기본 기계 코드로 컴파일됩니다. 일부에서는 C++의 컴파일러가 이식성을 감소시키고 새로운 대상 아키텍처를 위해 다시 빌드해야 한다고 주장하지만 C++ 코드는 거의 모든 프로세서 플랫폼에서 실행됩니다.

1995년에 처음 출시된 Java는 네이티브 코드로 직접 빌드되지 않습니다. 대신 Java는 JVM(Java Virtual Machine)에서 실행되는 중간 바이너리 표현인 바이트코드를 빌드합니다. 즉, Java 컴파일러의 출력을 실행하려면 플랫폼별 기본 실행 파일이 필요합니다.

C++와 Java는 일반적으로 구문이 C와 유사하기 때문에 C 유사 언어 계열에 속합니다. 가장 중요한 차이점은 생태계입니다. C++는 C 또는 C++ 또는 운영 체제의 API를 기반으로 라이브러리를 원활하게 호출할 수 있지만 Java는 Java 기반 라이브러리에 가장 적합합니다. JNI(Java Native Interface) API를 사용하여 Java의 C 라이브러리에 액세스할 수 있지만 오류가 발생하기 쉽고 일부 C 또는 C++ 코드가 필요합니다. C++는 저수준 언어이기 때문에 C++는 Java보다 하드웨어와 더 쉽게 상호 작용합니다.

세부 절충: 제네릭, 메모리 등

여러 관점에서 C++를 Java와 비교할 수 있습니다. 어떤 경우에는 C++와 Java 사이의 결정이 명확합니다. 앱이 게임이 아닌 경우 기본 Android 애플리케이션은 일반적으로 Java를 사용해야 합니다. 대부분의 게임 개발자는 가장 부드러운 실시간 애니메이션을 위해 C++ 또는 다른 언어를 선택해야 합니다. Java의 메모리 관리는 종종 게임 플레이 중에 지연을 일으킵니다.

게임이 아닌 크로스 플랫폼 애플리케이션은 이 논의의 범위를 벗어납니다. C++나 Java는 효율적인 GUI 개발을 위해 너무 장황하기 때문에 이 경우에 이상적이지 않습니다. 고성능 앱의 경우 C++ 모듈을 생성하여 무거운 작업을 수행하고 GUI용으로 개발자 생산성이 더 높은 언어를 사용하는 것이 가장 좋습니다.

게임이 아닌 크로스 플랫폼 애플리케이션은 이 논의의 범위를 벗어납니다. C++나 Java는 효율적인 GUI 개발을 위해 너무 장황하기 때문에 이 경우에 이상적이지 않습니다.

트위터

일부 프로젝트의 경우 선택이 명확하지 않을 수 있으므로 더 자세히 비교해 보겠습니다.

특징 C++ 자바
초심자 친화적 아니
런타임 성능 최고 좋은
지연 시간 예측 가능 예측할 수 없음
참조 카운팅 스마트 포인터 아니
글로벌 마크 앤 스윕 가비지 컬렉션 아니 필수의
스택 메모리 할당 아니
네이티브 실행 파일로 컴파일 아니
자바 바이트코드로 컴파일 아니
저수준 운영 체제 API와 직접 상호 작용 C 코드 필요
C 라이브러리와 직접 상호 작용 C 코드 필요
Java 라이브러리와 직접 상호 작용 JNI를 통해
표준화된 빌드 및 패키지 관리 아니 메이븐


표에서 비교한 기능 외에도 다중 상속, 제네릭/템플릿 및 리플렉션과 같은 객체 지향 프로그래밍(OOP) 기능에 중점을 둘 것입니다. 두 언어 모두 OOP를 지원합니다. Java는 이를 의무화하지만 C++는 전역 함수 및 정적 데이터와 함께 OOP를 지원합니다.

다중 상속

OOP에서 상속은 자식 클래스가 부모 클래스의 속성과 메서드를 상속하는 경우입니다. 한 가지 표준 예는 보다 일반적인 Shape 클래스에서 상속되는 Rectangle 클래스입니다.

 // Note that we are in a C++ file class Shape { // Position int x, y; public: // The child class must override this pure virtual function virtual void draw() = 0; }; class Rectangle: public Shape { // Width and height int w, h; public: void draw(); };

다중 상속은 자식 클래스가 여러 부모로부터 상속받는 경우입니다. 다음은 RectangleShape 클래스와 추가 Clickable 클래스를 사용하는 예입니다.

 // Not recommended class Shape {...}; class Rectangle: public Shape {...}; class Clickable { int xClick, yClick; public: virtual void click() = 0; }; class ClickableRectangle: public Rectangle, public Clickable { void click(); };

이 경우 두 가지 기본 유형이 있습니다. Shape (기본 유형 Rectangle ) 및 Clickable . ClickableRectangle 은 두 개체 유형을 구성하기 위해 둘 다에서 상속합니다.

C++는 다중 상속을 지원합니다. 자바는 그렇지 않습니다. 다중 상속은 다음과 같은 특정한 경우에 유용합니다.

  • 고급 DSL(도메인별 언어) 만들기.
  • 컴파일 타임에 정교한 계산을 수행합니다.
  • Java에서는 불가능한 방식으로 프로젝트 유형 안전성을 개선합니다.

그러나 다중 상속을 사용하는 것은 일반적으로 권장되지 않습니다. 가장 숙련된 C++ 프로그래머만 수행할 수 있는 템플릿 메타프로그래밍과 결합하지 않으면 코드가 복잡해지고 성능에 영향을 줄 수 있습니다.

제네릭 및 템플릿

모든 데이터 유형과 함께 작동하는 클래스의 일반 버전은 코드 재사용에 실용적입니다. 두 언어 모두 이 지원(제네릭을 통한 Java, 템플릿을 통한 C++)을 제공하지만 C++ 템플릿의 유연성은 고급 프로그래밍을 보다 안전하고 강력하게 만들 수 있습니다. C++ 컴파일러는 템플릿과 함께 다른 유형을 사용할 때마다 새로운 사용자 정의 클래스 또는 함수를 생성합니다. 또한 C++ 템플릿은 최상위 함수의 매개변수 유형에 따라 사용자 정의 함수를 호출할 수 있으므로 특정 데이터 유형이 특수화된 코드를 가질 수 있습니다. 이것을 템플릿 전문화라고 합니다. Java에는 이에 상응하는 기능이 없습니다.

반대로 제네릭을 사용할 때 자바 컴파일러는 유형 삭제라는 프로세스를 통해 유형이 없는 일반 객체를 생성합니다. Java는 컴파일 중에 유형 검사를 수행하지만 프로그래머는 유형 매개변수를 기반으로 제네릭 클래스 또는 메소드의 동작을 수정할 수 없습니다. 이것을 더 잘 이해하기 위해 C++에서 template<class T1, class T2> 템플릿을 사용하는 일반 std::string format(std::string fmt, T1 item1, T2 item2) 함수의 빠른 예를 살펴보겠습니다. 내가 만든 라이브러리:

 std::string firstParameter = "A string"; int secondParameter = 123; // Format printed output as an eight-character-wide string and a hexadecimal value format("%8s %x", firstParameter, secondParameter); // Format printed output as two eight-character-wide strings format("%8s %8s", firstParameter, secondParameter);

C++은 format 함수를 std::string format(std::string fmt, std::string item1, int item2) 으로 생성하지만 Java는 item1item2 에 대한 특정 stringint 객체 유형 없이 생성합니다. 이 경우 C++ 템플릿은 마지막으로 들어오는 매개변수가 int 라는 것을 알고 있으므로 두 번째 format 호출에서 필요한 std::to_string 변환을 수행할 수 있습니다. 템플릿이 없으면 두 번째 format 호출에서와 같이 숫자를 문자열로 인쇄하려는 C++ printf 문은 정의되지 않은 동작을 하고 응용 프로그램을 중단시키거나 쓰레기를 인쇄할 수 있습니다. Java 함수는 첫 번째 format 호출에서 숫자를 문자열로만 처리할 수 있으며 16진수 정수로 직접 형식을 지정하지 않습니다. 이것은 사소한 예이지만 클래스 또는 format 함수를 수정하지 않고 임의의 클래스 개체를 처리하기 위해 특수 템플릿을 선택하는 C++의 기능을 보여줍니다. 제네릭 대신 리플렉션을 사용하여 Java에서 출력을 올바르게 생성할 수 있지만 이 방법은 확장성이 적고 오류가 발생하기 쉽습니다.

반사

Java에서는 클래스 또는 클래스 유형에서 사용할 수 있는 멤버와 같은 구조적 세부 정보를 런타임에 찾을 수 있습니다. 이 기능을 반사라고 하는 이유는 아마도 내부에 무엇이 있는지 보기 위해 물체에 거울을 들고 있는 것과 같기 때문일 것입니다. (자세한 내용은 Oracle의 리플렉션 설명서에서 찾을 수 있습니다.)

C++에는 전체 리플렉션이 없지만 최신 C++는 RTTI(런타임 유형 정보)를 제공합니다. RTTI는 특정 개체 유형의 런타임 감지를 허용하지만 개체의 구성원과 같은 정보에는 액세스할 수 없습니다.

메모리 관리

C++와 Java의 또 다른 중요한 차이점은 메모리 관리로, 두 가지 주요 접근 방식이 있습니다. 개발자가 수동으로 메모리를 추적하고 해제해야 하는 수동; 자동, 여기서 소프트웨어는 사용되지 않은 메모리를 재활용하기 위해 아직 사용 중인 개체를 추적합니다. Java에서 예는 가비지 수집입니다.

Java는 가비지 수집 메모리를 필요로 하므로 수동 접근 방식보다 더 쉬운 메모리 관리를 제공하고 일반적으로 보안 취약성에 기여하는 메모리 해제 오류를 제거합니다. C++는 기본적으로 자동 메모리 관리를 제공하지 않지만 스마트 포인터라는 가비지 수집 형식을 지원합니다. 스마트 포인터는 참조 카운팅을 사용하며 올바르게 사용하면 안전하고 성능이 좋습니다. C++는 또한 개체가 파괴될 때 리소스를 정리하거나 해제하는 소멸자를 제공합니다.

Java는 힙 할당만 제공하지만 C++는 힙 할당( newdelete 또는 이전 C malloc 함수 사용)과 스택 할당을 모두 지원합니다. 스택은 선형 데이터 구조이고 힙은 트리 기반이므로 스택 메모리는 할당 및 해제가 훨씬 간단하기 때문에 스택 할당은 힙 할당보다 빠르고 안전할 수 있습니다.

스택 할당과 관련된 C++의 또 다른 장점은 RAII(Resource Acquisition Is Initialization)로 알려진 프로그래밍 기술입니다. RAII에서 참조와 같은 리소스는 제어 개체의 수명 주기와 연결됩니다. 리소스는 해당 개체의 수명 주기가 끝나면 파괴됩니다. RAII는 C++ 스마트 포인터가 수동 역참조 없이 작동하는 방식입니다. 함수의 맨 위에서 참조된 스마트 포인터는 함수를 종료할 때 자동으로 역참조됩니다. 스마트 포인터에 대한 마지막 참조인 경우 연결된 메모리도 해제됩니다. Java가 유사한 패턴을 제공하지만 특히 동일한 코드 블록에 여러 리소스를 생성해야 하는 경우 C++의 RAII보다 더 어색합니다.

런타임 성능

Java는 견고한 런타임 성능을 가지고 있지만 수동 메모리 관리가 실제 응용 프로그램의 가비지 수집보다 빠르기 때문에 C++가 여전히 왕좌를 차지하고 있습니다. Java는 JIT 컴파일로 인해 특정 코너 케이스에서 C++보다 성능이 뛰어날 수 있지만 C++는 대부분의 사소한 케이스에서 승리합니다.

특히 Java의 표준 메모리 라이브러리는 C++의 감소된 힙 할당 사용과 비교하여 할당으로 가비지 수집기를 과도하게 사용합니다. 그러나 Java는 여전히 상대적으로 빠르며 실시간 제약이 있는 게임이나 애플리케이션과 같이 대기 시간이 가장 중요한 문제가 아닌 한 허용되어야 합니다.

빌드 및 패키지 관리

Java의 성능이 부족한 부분을 사용 편의성을 보완합니다. 개발자 효율성에 영향을 미치는 한 가지 구성 요소는 빌드 및 패키지 관리입니다. 프로젝트를 빌드하고 애플리케이션에 외부 종속성을 가져오는 방법입니다. Java에서 Maven이라는 도구는 이 프로세스를 몇 가지 쉬운 단계로 단순화하고 IntelliJ IDEA와 같은 많은 IDE와 통합합니다.

그러나 C++에는 표준화된 패키지 저장소가 없습니다. 응용 프로그램에서 C++ 코드를 빌드하는 표준화된 방법조차 없습니다. 일부 개발자는 Visual Studio를 선호하고 다른 개발자는 CMake 또는 다른 사용자 지정 도구 집합을 사용합니다. 복잡성을 더하면 특정 상용 C++ 라이브러리는 이진 형식이며 이러한 라이브러리를 빌드 프로세스에 통합하는 일관된 방법이 없습니다. 또한 빌드 설정 또는 컴파일러 버전의 변형으로 인해 바이너리 라이브러리가 작동하도록 하는 데 문제가 발생할 수 있습니다.

초보자 친화성

빌드 및 패키지 관리 마찰이 C++가 Java보다 초보자에게 훨씬 덜 친숙한 유일한 이유는 아닙니다. 프로그래머는 C, 어셈블리 언어 또는 컴퓨터의 하위 수준 작업에 익숙하지 않은 경우 디버깅하고 C++를 안전하게 사용하는 데 어려움을 겪을 수 있습니다. C++를 강력한 도구처럼 생각하십시오. 많은 것을 성취할 수 있지만 잘못 사용하면 위험합니다.

앞서 언급한 Java의 메모리 관리 접근 방식은 C++보다 훨씬 더 쉽게 액세스할 수 있도록 합니다. Java 프로그래머는 언어가 자동으로 처리하므로 객체 메모리 해제에 대해 걱정할 필요가 없습니다.

결정 시간: C++ 또는 Java?

왼쪽 상단 모서리에 진한 파란색 "시작" 거품이 있는 순서도는 "예" 및 기타 옵션에 대한 진한 파란색 분기가 있는 일련의 흰색 결정 교차점을 통해 그 아래에 있는 7개의 연한 파란색 결론 상자 중 하나에 연결됩니다. "아니오"에 대한 밝은 파란색 가지. 첫 번째는 "Cross-platform GUI 앱?"입니다. "예"는 "교차 플랫폼 개발 환경을 선택하고 기본 언어를 사용하십시오."라는 결론을 나타냅니다. "아니요"는 "기본 Android 앱?"을 가리킵니다. 여기서 "예"는 "게임입니까?"라는 두 번째 질문을 가리킵니다. 두 번째 질문에서 "아니오"는 "자바(또는 Kotlin) 사용"이라는 결론을 가리키고 "예"는 "교차 플랫폼 게임 엔진을 선택하고 권장 언어를 사용하십시오"라는 다른 결론을 가리킵니다. "네이티브 안드로이드 앱?" 질문, "아니요"는 "기본 Windows 앱?"을 가리킵니다. 여기서 "예"는 "게임입니까?"라는 두 번째 질문을 가리킵니다. 두 번째 질문에서 "예"는 "크로스 플랫폼 게임 엔진을 선택하고 권장 언어를 사용하십시오"라는 결론을 가리키고 "아니오"는 "Windows GUI 환경을 선택하고 기본 환경을 사용하십시오"라는 다른 결론을 나타냅니다. 언어(일반적으로 C++ 또는 C#)." "기본 Windows 앱?" 질문, "아니요"는 "서버 앱?"을 가리킵니다. "예"는 두 번째 질문인 "개발자 유형?"을 가리킵니다. 두 번째 질문에서 "중간 기술" 결정은 "Java(또는 C# 또는 TypeScript) 사용"이라는 결론을 가리키고 "숙련" 결정은 세 번째 질문인 "최우선 순위?"를 가리킵니다. 세 번째 질문에서 "개발자 생산성" 결정은 "Java(또는 C# 또는 TypeScript) 사용"이라는 결론을 가리키고 "성능" 결정은 "C++(또는 Rust) 사용"이라는 다른 결론을 가리킵니다. "서버 앱?" "아니오"라는 질문은 "운전자 개발?"이라는 두 번째 질문을 가리킵니다. 두 번째 질문에서 "예"는 "C++(또는 Rust) 사용"이라는 결론을 가리키고 "아니오"는 세 번째 질문인 "IoT 개발?"을 가리킵니다. 3차 질문에서 "예"는 "C++(또는 Rust) 사용"이라는 결론을 가리키고 "아니오"는 4차 질문인 "고속 거래?"를 가리킵니다. 4차 질문에서 "예"는 "C++(또는 Rust) 사용"이라는 결론을 가리키고 "아니오"는 마지막 남은 결론인 "대상 도메인에 대해 잘 아는 사람에게 물어보세요."를 가리킵니다.
다양한 프로젝트 유형에 가장 적합한 언어를 선택하기 위한 확장된 가이드입니다.

이제 C++와 Java의 차이점을 자세히 살펴보았으므로 원래 질문인 C++ 또는 Java로 돌아갑니다. 두 언어에 대한 깊은 이해에도 불구하고 모든 상황에 맞는 정답은 없습니다.

저수준 프로그래밍 개념에 익숙하지 않은 소프트웨어 엔지니어는 게임과 같은 실시간 컨텍스트를 제외하고 C++ 또는 Java로 결정을 제한할 때 Java를 선택하는 것이 더 나을 수 있습니다. 반면에 시야를 넓히려는 개발자는 C++를 선택하여 더 많은 것을 배울 수 있습니다.

그러나 C++와 Java 간의 기술적인 차이는 결정의 작은 요소일 뿐입니다. 특정 유형의 제품에는 특별한 선택이 필요합니다. 여전히 확실하지 않은 경우 순서도를 참조할 수 있지만 궁극적으로 제3 언어를 가리킬 수 있다는 점을 염두에 두십시오.