C++
C++ 는 범용 프로그래밍 언어입니다. 하드웨어를 직접 만지는 시스템 프로그래밍 쪽으로 치우쳐 있습니다. 무엇을 C++ 라고 부를지는 국제 표준 문서가 정합니다. 받아서 설치하는 것은 그 요구를 구현한 쪽입니다.
상세
isocpp.org 의 공식 FAQ(Frequently Asked Questions)는 C++ 를 시스템 프로그래밍 쪽으로 치우친 범용 프로그래밍 언어라고 적습니다. "더 나은 C" 라는 말도 같은 자리에 붙어 있습니다. 지원하는 것으로 네 가지를 듭니다. 데이터 추상화, 객체지향 프로그래밍, 제네릭 프로그래밍, 함수형 프로그래밍입니다. 각각의 예로 클래스, 상속, 재사용 가능한 제네릭 컨테이너와 알고리즘, 템플릿 메타프로그래밍과 람다 함수와 constexpr 를 듭니다. 같은 FAQ 는 이 언어가 ISO(International Organization for Standardization) 표준으로 정의된다고 적습니다. 수십 년에 걸친 안정성을 제공한다는 말도 덧붙입니다.
표준 문서와 구현체
표준 초안의 [intro.scope] 는 "이 문서는 C++ 구현체에 대한 요구사항을 규정한다" 로 시작합니다.
첫 번째 요구사항은 "구현체가 그 언어를 구현한다" 는 것입니다. 그래서 이 문서가 C++ 자체를
정의하기도 한다고 적습니다. 나머지 요구사항과 그 완화는 문서 여기저기에 흩어져 있습니다.
규격을 적은 문서가 한쪽에 있고, 그 요구를 만족시킨 물건이 다른 쪽에 있다는 뜻입니다. 손에 잡히는 것은 뒤쪽입니다.
flowchart TD
S["표준 문서"] -->|요구사항| I["구현체"]
P["규칙을 어기지 않은 프로그램"] --> I
I -->|받아들여 올바르게 실행| R["실행"]
I -.->|구현체의 한계를 넘으면| X["예외"]
[intro.compliance.general] 은 그 관계를 프로그램 쪽 말로 바꿔 적습니다. 진단 가능한 규칙의
집합은 표준 문서의 모든 구문 규칙과 의미 규칙에서 "진단이 요구되지 않는다" 는 명시적 표기가
붙은 것과 미정의 동작으로 기술되는 것을 뺀 나머지입니다. 그리고 어떤 프로그램이 [lex] 절부터
[exec] 절까지의 규칙과 부록 [depr] 의 규칙을 하나도 어기지 않으면, 준수하는 구현체는 그
프로그램을 받아들여 올바르게 실행해야 합니다. 구현체의 한계를 넘어서는 경우만 예외입니다.
포기한 것
쓰지 않는 기능에 물리는 비용
isocpp.org 의 FAQ 는 zero-overhead 원칙을 C++ 설계의 지침 원칙이라고 적습니다. 문장은 둘입니다. 쓰지 않는 것에는 시간으로도 공간으로도 값을 물리지 않는다. 쓰는 것에 대해서는 손으로 직접 짜더라도 그보다 낫게 만들 수 없다.
FAQ 는 이것을 다시 풀어 적습니다. 새 기능을 쓰지 않는 기존 코드를 더 크거나 느려지게 만드는 기능은 C++ 에 추가되지 않아야 합니다. 그 기능을 쓰지 않은 프로그래머의 코드만 못한 코드를 컴파일러가 생성하게 되는 기능도 추가되지 않아야 합니다.
얻은 것은 안 쓰는 기능의 값을 안 치른다는 것입니다. 안 하기로 한 것은 모든 코드에 값이 걸리는 장치를 언어가 기본으로 쥐는 일입니다. 배열 접근에 범위 검사를 두지 않은 자리가 그 결과입니다. 표준이 규정한 가비지 컬렉션이 걷힌 사유는 이것과 다릅니다. 그쪽은 해당 절이 따로 적습니다.
배열 접근의 범위 검사
FAQ 는 C++ 가 정적 타입 안전 규칙을 깨는 연산을 지원하는 이유를 셋으로 답합니다. 하드웨어에 직접 접근하기 위해서, 최적의 실행 시간과 공간 성능을 얻기 위해서, 그리고 C 와 호환되기 위해서입니다. 첫째의 예로는 정수를 장치 레지스터의 주소로 다루는 것을, 둘째의 예로는 배열 원소에 대한 검사 없는 접근과 포인터를 통한 객체에 대한 검사 없는 접근을 듭니다.
같은 FAQ 가 미정의 동작의 예로 드는 코드입니다. 아마도 가장 잘 알려지고 가장 악명 높은 예일 것이라고 적습니다.
int a[10];
a[100] = 0; // range error
int* p = a;
// ...
p[100] = 0; // range error (unless we gave p a better value before that assignment)
C++ 와 C 의 배열·포인터 개념은 기계의 메모리와 주소 개념을 오버헤드 없이 그대로 나타낸 것이라고 적습니다. 포인터에 대한 기본 연산은 기계 명령에 곧장 대응됩니다. 특히 범위 검사는 수행되지 않습니다. 범위 검사를 하면 실행 시간과 코드 크기에서 비용이 들기 때문입니다.
무엇이 미정의로 남는지도 같은 자리에서 답합니다. 기계마다 다르고 C 가 많은 것을 미정의로 뒀기 때문입니다. C++ 는 C 처럼 하드웨어를 직접 그리고 효율적으로 이용하려는 것이고, 그러려면 비트·바이트·워드·주소·정수 연산·부동소수점 연산 같은 하드웨어 실체를 우리가 바랐으면 하는 모습이 아니라 주어진 기계에서 실제로 그런 모습대로 다뤄야 한다고 적습니다.
그 자리가 무엇이 되는지는 표준 초안이 정의합니다. [defns.undefined] 는 미정의
동작(undefined behavior)을 "이 문서가 아무 요구사항도 부과하지 않는 동작" 이라고 적습니다.
허용되는 미정의 동작의 폭은 넓습니다. 한쪽 끝은 상황을 완전히 무시하고 예측 불가능한 결과를
내는 것입니다. 그 사이에는 번역이나 실행 중에 그 환경의 특성에 맞는 문서화된 방식으로
동작하는 것이 있습니다. 다른 쪽 끝은 진단 메시지를 내며 번역이나 실행을 끝내는 것입니다.
다만 잘못된 프로그램 구성 중 상당수는 미정의 동작을 일으키지 않습니다. 그런 것들은 진단이
요구된다고 같은 주석이 적습니다.
표준이 규정한 가비지 컬렉션
표준 위원회 제안 문서 P2186R2 는 C++ 의 가비지 컬렉션 지원을 폐기 예고가 아니라 제거하자고
제안합니다. 대상은 declare_reachable·undeclare_reachable·declare_no_pointers·
undeclare_no_pointers·get_pointer_safety 다섯 라이브러리 함수, pointer_safety 열거형,
__STDCPP_STRICT_POINTER_SAFETY__ 매크로, 그리고 코어 언어 조문입니다.
문서가 적는 경위는 이렇습니다. 최소한의 가비지 컬렉션 지원은 2008년 N2670 으로 C++0x 에 들어갔고, 주된 추가는 "엄격한 포인터 안전성" 개념과 그것을 위한 라이브러리 지원이었습니다. C++ 용 가비지 컬렉터 자체는 성공한 것들이 있습니다. Boehm GC(Garbage Collector)와, 가상 머신이 C++ 로 구현된 언어 가상 머신 안의 수집기들이 그 예로 실립니다.
제거의 근거는 그 둘이 다르다는 데 있습니다. 문서는 C++ 의 가비지 컬렉션이 특정 응용에 분명히 쓸모가 있지만 표준이 규정한 형태의 가비지 컬렉션은 그 응용들에 쓸모가 없다고 적습니다. 저자들은 엄격한 포인터 안전성 기능의 구현을 하나도 알지 못하고, 그것을 쓰는 곳도 알지 못한다고 적습니다. libc++ 와 libstdc++ 와 마이크로소프트 표준 라이브러리는 모두 완화된 포인터 안전성만 제공합니다. 코어 조문이 구현체에 아무 제약도 주지 않는 상태였고, 구현체들은 그런데도 약한 쪽을 골랐다는 것입니다. 2020년 7월 30일 원격 회의 표결은 강한 찬성 3, 찬성 9, 중립 4, 반대 0, 강한 반대 1 이었습니다.
예시
LLVM
llvm.org 의 코딩 표준 문서는 달리 문서화되지 않는 한 LLVM 하위 프로젝트가 표준 C++17 코드로 작성되며 불필요한 벤더 고유 확장을 피한다고 적습니다. 그러면서도 호스트 컴파일러로 지원하는 주요 툴체인에서 쓸 수 있는 기능으로 스스로를 제한한다고 덧붙입니다.
Chromium
Chromium 스타일 가이드의 「Modern C++ features」 절은 2026년 1월 기준으로 Chromium 스타일이 C++23 을, Google 스타일이 C++20 을 목표로 한다고 적습니다. 지원되는 C++ 판의 기능 중 일부는 여전히 금지된다고도 적습니다.
Unreal Engine
Epic Games 의 공식 문서는 게임플레이 요소를 C++ 코드로 프로그래밍할 때 모듈 하나가 여러 C++ 클래스를 담을 수 있고 각 클래스가 새 Actor 나 Object 의 틀을 정의한다고 적습니다. 클래스 헤더 파일 안에서 클래스와 그 클래스의 함수와 프로퍼티를 선언합니다. 언리얼 엔진으로 프로그래밍할 때 표준 C++ 클래스와 함수와 변수를 그대로 둘 수 있고, 표준 C++ 문법으로 정의할 수 있다고도 적습니다.
세 문서 모두 C++ 를 왜 골랐는지는 적지 않습니다. C++ 로 쓴다는 사실과, 어느 판을 기준으로 삼는지까지만 밝힙니다.
배경
Bjarne Stroustrup 은 케임브리지에서 시뮬레이터를 짰습니다. 그 일로 Simula 의 타입 시스템이 지닌 표현력에 큰 존경을 갖게 됐다고 적습니다. 그 컴파일러가 타입 오류를 잡아내는 능력도 마찬가지였습니다. 클래스와 코루틴 기구, 그리고 포괄적인 타입 검사가 있었습니다. 덕분에 문제와 오류가 프로그램 크기에 비례해서 또는 그 이상으로 늘어나지 않았습니다. 그런데 Simula 의 구현은 같은 방식으로 확장되지 않아 프로젝트가 재난에 가까워졌습니다. 따로 컴파일한 클래스의 링크 시간이 형편없었습니다. 실행 성능은 그 시뮬레이터로 실제 데이터를 얻을 가망이 없는 수준이었습니다. 이 오버헤드 문제는 Simula 에 근본적이어서 고칠 수 없었습니다. 비용은 여러 언어 기능과 그 상호작용에서 나왔습니다. 런타임 타입 검사, 변수의 초기화 보장, 동시성 지원입니다. 사용자가 할당한 객체와 프로시저 활성화 레코드 양쪽에 대한 가비지 컬렉션도 그중 하나입니다. 그 논문에는 측정 하나가 실려 있습니다. 자원 관리가 시뮬레이션 대상 시스템의 일부라 쓰레기가 전혀 생기지 않았습니다. 그런데도 시간의 80% 넘게가 가비지 컬렉터에서 소모됐습니다.
프로젝트를 접지 않으려고 그는 시뮬레이터를 BCPL(Basic Combined Programming Language)로 다시 짜 실험용 CAP 컴퓨터에서 돌렸습니다. BCPL 로 코딩하고 디버깅한 경험은 끔찍했다고 적습니다. BCPL 은 C 를 아주 고수준 언어처럼 보이게 만듭니다. 타입 검사도 런타임 지원도 전혀 제공하지 않습니다. 대신 결과물은 적당히 빠르게 돌았습니다. 케임브리지를 떠날 때 그는 다짐 하나를 했습니다. 다시는 그때만큼 부적합한 도구로 문제에 덤비지 않겠다는 것입니다. 도구가 갖춰야 할 조건 셋도 적었습니다. 첫째는 프로그램 조직화를 위한 Simula 의 지원입니다. 클래스, 어떤 형태의 클래스 계층, 어떤 형태의 동시성 지원, 클래스에 기반한 타입 시스템의 강한 정적 검사를 말합니다. 둘째는 BCPL 프로그램만큼 빠르게 도는 결과물입니다. 따로 컴파일한 단위를 쉽게 하나의 프로그램으로 합치는 BCPL 의 능력도 여기 들어갑니다. 셋째는 이식성 높은 구현입니다.
1979년 그는 나중에 C++ 가 되는 것에 착수했습니다. 처음 이름은 "C with Classes" 였습니다. Simula67 이 권하는 스타일로 효율적인 시스템 프로그램을 쓰려는 것이 목적이었고, 그래서 더 나은 타입 검사와 데이터 추상화와 객체지향 프로그래밍 기능을 C 에 더했습니다. 착수를 부른 구체적인 과제는 운영체제 기능을 네트워크에 걸쳐 분산하는 일이었습니다.
C 와 호환되느냐는 물음에 isocpp.org 의 FAQ 는 "거의" 라고 답합니다. C++ 는 가능한 한 C 와
호환되지만 그 이상은 아니라고 적습니다. 실제로 가장 큰 차이는 둘입니다. C++ 는 프로토타입을
요구합니다. 그리고 f() 는 매개변수를 받지 않는 함수를 선언합니다. C 에서는 f() 로 선언한
함수에 임의 타입의 매개변수를 임의 개수로 넘길 수 있습니다.
사람들은 "C with Classes" 를 "새 C" 로 부르다 그냥 C 라 부르기 시작했습니다. 그러자 원래 C
쪽이 "평범한 C" 나 "옛 C" 로 불리게 됐습니다. 경영진의 정중한 요청으로 C84 라는 이름이
붙었습니다. 그 이름은 몇 달만 쓰였습니다. 못생긴 데다 관공서 같고, 사람들이 84 를 떼면 혼란이
그대로 남기 때문입니다. 새 이름을 구하던 중
Rick Mascitti 가 제안한 C++ 를 골랐습니다. 짧고, 해석이 여러 가지로 붙고, "형용사 + C" 꼴이
아니었기 때문입니다. C 에서 ++ 는 맥락에 따라 "다음" 이나 "후속" 이나 "증가" 로 읽힙니다.
관련 항목
한 언어 안에서 겹쳐 쓰는 프로그래밍 방식
시스템 프로그래밍 · 데이터 추상화 · 객체지향 프로그래밍 · 제네릭 프로그래밍 · 함수형 프로그래밍
메모리를 손으로 다루게 하는 장치
포인터 · RAII(Resource Acquisition Is Initialization) · 스마트 포인터 · 이동 시맨틱스 · 메모리 모델
코드를 재사용하게 하는 장치
템플릿 · 가상 함수 · constexpr · 람다 · 표준 라이브러리
오버헤드 없이 그대로 다루는 하드웨어 실체
레지스터 · 비트 · 바이트 · 워드 · 주소 · 정수 연산 · 부동소수점 연산
C++를 표준으로 정하는 기구·문서
ISO(International Organization for Standardization) · WG21(ISO/IEC JTC1/SC22/WG21, C++ 표준 위원회) · ISO/IEC 14882
표준 문서가 규정하는 프로그램 규칙
미정의 동작 · 번역 단위 · ODR(one-definition rule) · 폐기 예고
표준이 걷어낸 GC 지원의 부품
열거형 · 매크로 · declare_reachable · undeclare_reachable · declare_no_pointers · undeclare_no_pointers · get_pointer_safety · pointer_safety · __STDCPP_STRICT_POINTER_SAFETY__
GC 지원이 오간 표준·제안 문서
N2670 · P2186R2 · C++0x
표준 없이 대신 쓰는 자동 회수 수단
가비지 컬렉션 · 가비지 컬렉터 · Boehm GC · 가상 머신
표준 라이브러리를 실제로 구현한 사례
libc++ · libstdc++ · 마이크로소프트
컴파일에서 실행까지 거치는 도구·규약
컴파일러 · 링커 · ABI(Application Binary Interface) · 툴체인
실제로 이 언어를 채택한 사례
LLVM · Chromium · 언리얼 엔진 · Google
C++가 비롯된 배경
Simula · BCPL(Basic Combined Programming Language) · C · 운영체제 · 네트워크 · Bjarne Stroustrup · Rick Mascitti · C84 · AT&T
C++가 설계 조건으로 세운 성질
다른 이름: C with Classes