본문 바로가기

C++ 언어 소개

C++이 C에서 어떻게 출발해 표준화되었는지, 객체 지향과 제네릭 프로그래밍, 자원을 객체의 수명에 묶는 RAII를 어떻게 함께 제공하는지 살펴보고, 성능과 하드웨어 제어가 필요한 문제에서 C++을 고를지 판단하는 기준을 세웁니다.

C++은 운영체제의 주변 계층과 게임 엔진, 브라우저, 데이터베이스처럼 빠르게 동작해야 하고 메모리와 하드웨어를 세밀하게 다뤄야 하는 프로그램에서 오래 쓰여 온 언어입니다. 그만큼 배우기 쉬운 언어는 아니어서, 메모리를 직접 관리하는 일과 길고 낯선 컴파일 오류, 템플릿 같은 주제에서 처음에는 쉽게 지치기도 합니다.

그래서 문법을 익히기 전에, C++이 어떤 문제를 풀려고 만들어졌고 어떤 비용을 요구하는지부터 봅니다. 이 글은 C++의 역사와 특징을 정리하고, 어떤 프로그램에서 C++을 고를 만한지 판단하는 기준을 세웁니다.

C++이 생긴 과정

C++은 1979년 벨 연구소의 비야네 스트롭스트룹(Bjarne Stroustrup)이 C 언어에 클래스를 더한 C with Classes라는 이름으로 만들기 시작했고, 1983년에 지금의 C++이라는 이름이 붙었습니다. 이름의 ++는 변수의 값을 하나 늘리는 C의 증가 연산자로, C에서 한 걸음 나아간 언어라는 뜻을 담았습니다.

C++은 C와 많은 문법을 공유하고, 포인터로 메모리를 직접 다루는 C의 저수준 모델도 이어받았습니다. 하지만 C의 모든 코드를 그대로 받아들이는 상위 집합은 아니며, 자기만의 타입 규칙과 추상화 도구를 갖춘 별개의 언어입니다. 예를 들어 C에서는 허용되는 void*에서 다른 포인터로의 자동 변환을 C++은 허용하지 않습니다.

1998년에 국제 표준화 기구(ISO)가 첫 표준인 C++98을 정한 뒤, C++11부터는 3년마다 새 표준이 나오고 있습니다. C++11은 자동 타입 추론과 람다, 이동 의미론, 스마트 포인터를 더해 이전과 많이 달라진 언어가 되었고, C++20은 콘셉트와 범위(ranges), 모듈, 코루틴을 더했습니다. 2024년에는 C++23이 ISO/IEC 14882:2024로 발행되었으며, 이 교재는 주요 컴파일러가 널리 지원하는 C++20을 기준으로 설명하고 C++23의 기능은 그 자리에서 표시합니다.

C++이 함께 제공하는 도구

C++은 하나의 방식만 강요하지 않고, 서로 다른 성격의 도구를 한 언어 안에서 함께 씁니다.

첫째는 하드웨어에 가까운 제어입니다. 포인터로 메모리의 주소를 직접 다루고, 객체를 메모리의 어디에 어떤 배치로 둘지 정할 수 있으며, 운영체제나 장치가 제공하는 C 인터페이스를 그대로 부를 수 있습니다. 이 제어 덕분에 C++ 프로그램은 실행할 때 가상 머신이나 가비지 컬렉터 없이 기계어로 바로 실행됩니다.

둘째는 추상화의 도구입니다. 클래스와 상속, 가상 함수로 객체 지향 프로그래밍(object-oriented programming)을 하고, 템플릿으로 여러 타입에 같은 알고리즘을 적용하는 제네릭 프로그래밍(generic programming)을 합니다. C++은 이런 추상화가 손으로 직접 짠 코드보다 느리지 않도록 설계한다는 원칙을 따르며, 쓰지 않는 기능에는 비용을 치르지 않고 쓰는 기능은 손으로 짠 것만큼 효율적이어야 한다는 뜻에서 이를 영 오버헤드 원칙(zero-overhead principle)이라고 부릅니다.

셋째는 RAII(Resource Acquisition Is Initialization)입니다. 메모리나 파일, 잠금 같은 자원을 객체가 만들어질 때 얻고, 객체가 사라질 때 자동으로 돌려주게 해서 정리를 잊는 실수를 줄입니다. 지금은 이름만 기억해 두고, 객체의 수명과 소멸자를 배우는 뒤의 장에서 자세히 다룹니다.

C++이 쓰이는 곳

C++은 성능과 제어가 함께 필요한 곳에서 많이 쓰입니다. Unreal Engine은 C++을 주된 개발 언어로 쓰고, 사용자 스크립트를 C#으로 쓰는 Unity도 엔진의 내부는 C++로 만들어졌습니다. Chrome과 Firefox의 렌더링 엔진, MySQL과 MongoDB의 핵심, PyTorch와 TensorFlow의 연산 라이브러리, 고빈도 매매 시스템도 C++로 작성된 부분이 큽니다.

운영체제에서도 쓰이지만, Linux 커널은 대부분 C로 작성되어 있습니다. C++은 그 주변의 드라이버와 런타임, 시스템 도구, Windows와 macOS의 여러 구성 요소에서 쓰입니다. 이런 분야들의 공통점은 정해진 시간 안에 많은 계산을 해야 하거나, 메모리를 아껴야 하거나, 기존의 C와 C++ 라이브러리와 직접 맞물려야 한다는 것입니다.

C++을 고르는 기준

C++이 모든 프로젝트의 정답은 아닙니다. 화면과 요구사항이 매주 바뀌는 서비스처럼 개발 속도가 가장 중요한 곳에서는, 메모리 관리와 빌드 설정에 드는 비용이 C++의 장점보다 클 수 있습니다.

C++ · BUILD AND EXECUTION

C++ 선택은 측정한 요구와 관리 비용을 함께 본다

지연·처리량·메모리 목표와 장치·ABI·기존 라이브러리 요구를 확인하고, 팀이 유지할 수 있는 경계를 정합니다.

C++ 선택은 측정된 제약에서 시작한다성능 목표와 네이티브 제어, 팀의 수명 관리 역량을 차례로 확인한다. 요구 측정실제 병목인가네이티브 제어팀이 비용을감당하는가C++ 경계를 채택먼저 프로파일아니요 경로경계를 더 작게아니요 경로아니요예아니요예
C++ 선택의 두 분기측정 후 실제 병목이 아니면 먼저 프로파일한다. 병목이면 네이티브 제어 요구를 확인한다. 팀이 비용을 감당하지 못하면 경계를 작게 하고, 감당하면 C++ 경계를 채택한다.요구 측정지연 · 처리량 · 메모리실제 병목인가먼저 프로파일네이티브 제어장치 · ABI라이브러리팀이 비용을감당하는가경계를 더 작게C++ 경계를 채택작고 명시적인인터페이스아니요예아니요예
  1. 요구 측정

    지연·처리량·메모리

  2. 실제 병목인가

    아니요면 먼저 프로파일하고, 예라면 네이티브 제어 필요를 확인한다.

  3. 네이티브 제어

    장치·ABI·기존 라이브러리 경계를 확인한다.

  4. 팀이 비용을 감당하는가

    아니요면 경계를 더 작게 하고, 예라면 채택한다.

  5. C++ 경계를 채택

    작고 명시적인 인터페이스로 제한한다.

선택을 검토할 세 가지 근거

성능 목표와 측정값, 병목 위치를 비교합니다.

제어 필요한 장치·ABI·라이브러리 경계를 확인합니다.

유지 비용 객체 수명·소유권, 빌드·디버깅, 배포를 팀이 관리할 수 있는지 봅니다.

성능 목표와 네이티브 제어, 팀의 수명 관리 역량을 차례로 확인한다.

그림처럼 먼저 지연, 처리량, 메모리의 목표와 실제 측정값을 비교합니다. 그 차이가 실제 병목에서 오지 않는다면 언어를 바꾸기 전에 프로파일러로 원인부터 찾고, 병목이 맞다면 장치나 ABI, 기존 라이브러리처럼 네이티브 코드로만 다룰 수 있는 요구가 있는지 확인합니다. 마지막으로 팀이 객체의 수명과 소유권, 빌드와 디버깅, 배포를 감당할 수 있는지 보고, 감당하기 어렵다면 C++로 쓰는 부분을 작고 분명한 인터페이스 뒤로 줄입니다.

예를 들어 센서 데이터를 처리하는 프로그램의 목표 지연이 10ms인데 측정한 지연이 18ms이고, 그중 15ms가 외부 장치의 응답을 기다리는 시간이라고 해 봅시다. 이때는 언어를 바꿔도 장치의 응답 시간이 줄지 않으므로 C++을 고를 이유가 되지 않습니다. 반대로 대부분의 시간이 프로그램 안의 계산이고 장치 라이브러리가 C++ 인터페이스를 요구한다면, 그 계산 부분을 C++로 구현할 이유가 생깁니다. 이 숫자는 판단을 연습하기 위한 가정입니다.

정리

  • C++은 C에 클래스를 더하며 시작했지만, C의 상위 집합이 아닌 자기만의 타입 규칙을 가진 언어이며, C++11부터 3년마다 새 표준이 나옵니다.
  • 이 교재는 주요 컴파일러가 널리 지원하는 C++20을 기준으로 하고, C++23의 기능은 그 자리에서 표시합니다.
  • C++은 포인터와 메모리 배치 같은 저수준 제어와, 클래스와 템플릿 같은 추상화, 자원을 객체의 수명에 묶는 RAII를 한 언어에서 제공합니다.
  • 영 오버헤드 원칙은 쓰지 않는 기능에 비용을 치르지 않고, 쓰는 추상화는 손으로 짠 코드만큼 효율적이어야 한다는 설계 원칙입니다.
  • C++은 측정한 병목이 계산에 있고, 네이티브 제어가 필요하며, 팀이 그 비용을 감당할 수 있을 때 고르고, 그렇지 않으면 C++로 쓰는 범위를 줄입니다.

연습 문제

1. C와 C++의 관계

"C++은 C의 상위 집합이므로 모든 C 코드는 C++ 컴파일러로 그대로 컴파일된다"는 말은 맞나요?

해설 보기

맞지 않습니다. C++은 C와 많은 문법을 공유하지만 별개의 언어여서, int* p = malloc(sizeof(int));처럼 malloc이 돌려준 void*를 다른 포인터에 그대로 대입하는 코드는 C에서는 허용되지만 C++에서는 명시적인 변환이 필요합니다. new나 class처럼 C++에서 예약어가 된 이름을 변수 이름으로 쓴 C 코드도 C++로는 컴파일되지 않습니다.

2. 예약 서비스와 C++

매주 화면과 요구사항이 바뀌는 예약 서비스가 있습니다. 성능을 측정한 값은 없고, 팀은 C++의 빌드와 메모리 관리를 다뤄 본 적이 없습니다. 이 서비스를 C++로 새로 만들 이유가 충분한가요?

해설 보기

충분하지 않습니다. 성능을 측정하지 않았으므로 계산이 병목이라는 사실이 확인되지 않았고, 팀이 C++의 수명 관리와 빌드를 감당할 수 있는지도 알 수 없습니다. 먼저 목표와 측정값을 비교해 병목을 찾고, 정말 계산이 문제인 부분이 있다면 그 부분만 작은 인터페이스 뒤에 C++로 구현할 수 있는지 검토합니다.

3. 기다리는 시간이 대부분인 프로그램

본문의 센서 예에서 측정한 지연 18ms 가운데 계산이 15ms이고 장치의 응답이 3ms였다면 판단은 어떻게 달라지나요?

해설 보기

이번에는 지연의 대부분이 프로그램 안의 계산이므로, 계산을 빠르게 하는 것이 목표 10ms에 다가가는 방법이 됩니다. 알고리즘과 메모리 사용을 먼저 개선해 보고, 그래도 부족하며 장치 라이브러리와 직접 맞물려야 한다면 그 계산 부분을 C++로 구현하는 것을 검토할 만합니다.

다음 글 개발 환경 설정에서는 Windows와 macOS, Linux에서 C++ 컴파일러와 편집기를 설치하고, 설치한 컴파일러의 버전과 표준 설정을 확인합니다.