C++

  하위 문서: C++/문법

#!syntax cpp
#include <iostream>

int main ()
{
	std::cout << "Hello, world!" << std::endl;
}

[1]

TIOBE에서 선정한 검색어 점유율 상위 10개 프로그래밍 언어

Java
[[C(프로그래밍 언어)

C]]

C++

Python

VB .NET

C#

PHP

JavaScript

SQL

Swift

2018년 10월 기준입니다. 자세한 내용은 TIOBE에서 확인할 수 있으며, 기준은 다음과 같습니다.

1. 개요
2. 특징
2.1. C와의 차이점
2.2. C++에서의 객체지향 프로그래밍
2.3. C++에서의 일반화 프로그래밍
2.3.1. 템플릿 메타 프로그래밍
2.4. 성능
2.5. 단점
2.5.1. 난이도
2.5.2. 애매한 위치
2.5.3. 성능 저하 요소
2.5.3.1. RTTI
2.5.3.2. 예외
3. 역사
3.1. 클래식 C++
3.1.1. C with Classes (1979년)
3.1.2. C++ (1983년)
3.1.3. C++ 2.0 (1989년)
3.1.4. C++98 (1998년)
3.1.5. C++03 (2003년)
3.2. 모던 C++
3.2.1. C++TR1 (2007년)
3.2.2. C++11 (2011년)
3.2.3. C++14 (2014년)
3.2.4. C++17 (2017년)
3.2.5. C++20 (2020년)
4. 방언 및 확장
6. 도서
7. 관련 문서
8. 외부 링크

1. 개요

C언어를 부분집합으로 갖고 객체 지향 및 일반화 프로그래밍과 같은 멀티 패러다임을 지원하는 프로그래밍 언어이다. 즉, C언어의 문법과 기능을 모두 사용할 수 있다. 다만, 오래된 표준은 부분집합으로 갖지 않았던 적도 있다. 1979년에 C언어에서 직접적으로 파생된 C with Classes라는 이름의 언어로 시작되었다가, 1983년에 지금의 이름을 갖게 되었다.

2. 특징

비아르네 스트로우스트루프(Bjarne Stroustrup)[2]C언어를 바탕으로 만들었다. C 계열 언어에서 "++"라는 것은 1을 더해서 원래 변수에 대입하라는 뜻이다.[3] C는 B 언어를 계승한다는 의미에서 C가 되었는데 왜 C++가 D가 아니라 C++가 되었냐 하면 C 언어를 거의 그대로 두고 필요한 만큼만 향상시켰기 때문이다. 그래서 C에서 되는 게 C++에서도 된다. 비슷한 이름인 C#과의 차이도 이러한 점에서 나타난다.

C++ 커뮤니티는 C++가 단순히 객체지향 프로그래밍 언어라고 분류되는 것을 오류라고 여긴다. C++의 제작자인 Bjarne Stroustrup은 C++가 단순한 객체 지향 프로그래밍 언어가 아니라고 설명했다. # C++는 프로그래머의 자유도가 높은 언어로써, 객체 지향이나 절차 지향 등의 설계에 제한을 두지 않는다. 다만, 객체지향과 일반화 프로그래밍을 사실상 거의 주된 패턴으로 사용하기 때문에 (프로그래머 입장에서는) 객체지향 언어에 가깝다. C++에서는 객체지향 프로그래밍 패러다임과 동등한 강도로 일반화 프로그래밍 패러다임을 강조하고 있다. 스트로우스트루프의 책 The C++ Programming Language 4판을 참고하면 "3부 추상화 메커니즘" 단원의 절반(7개 섹션)은 객체지향, 나머지 절반(7개 섹션)은 일반화 프로그래밍에 할애하고 있다. 그리고 "4부 표준 라이브러리" 단원의 1/3이 STL이고, 2/3는 나머지 표준 라이브러리를 설명하고 있다. 쉽게 말하자면 C++는 기존의 C 문법이 대표하는 절차적 프로그래밍, 클래스가 대표하는 객체지향 프로그래밍, 템플릿이 대표하는 일반화 프로그래밍의 세 기둥으로 지지되고 있는 언어이다.

유사한 의견으로 Effective C++의 저자 스콧 마이어스는 C++가 4가지 하위언어의 연합체라고 언급한다. C, 객체지향 C++, 템플릿 C++, STL. 현재의 모던 C++ 표준 라이브러리[4]가 스테파노프(Stephanov)와 리(Lee)가 1990년대에 개발한 Standard Template Library(STL)의 아이디어를 많이 수용한 것은 사실이나, C++의 표준안 그 어디에도 STL이라는 표현은 등장하지 않는다. Effective C++의 저자인 스콧 마이어스(Scott Meyers)와 같이 나이 많은 거장 프로그래머들이 1990년대의 관습 그대로 STL이라는 용어를 자신의 저작물에 지속적으로 사용하는 바람에 STL이라는 용어가 아직도 널리 사용되고 있으나, STL과 C++ 표준 라이브러리의 차이가 뭔지 묻는 수많은 구글 검색 결과가 보여주듯이 초보자들에게 꽤 커다란 혼돈을 주는 요소이다. 스테파노프와 리가 직접 작성한 Hewlet-Packard 버전의 STL이나, 실질적으로 더 널리 쓰이던 Silicon Graphics 버전의 SGI STL은 더 이상 관리가 되지 않고 방치가 된 지 오래이고, SGI STL을 계승하려고 노력하던 STLPort도 개발이 중단되었다. 결국 초창기 STL의 구현체는 모두 사라지고 그 아이디어만이 살아남아서 C++의 표준 라이브러리에 흡수되었으므로, STL을 따로 떼어 지칭하는 것이 이제 의미가 없다. 각각의 '하위 언어' 들의 규칙과 구현이 전부 다르므로 복잡도가 상당히 높으며 'C with classes'라는 초기 명칭과는 한참 차이가 날 정도로 확장되었다.

쓸 때는 C++라고 쓰지만 읽을 때는 시 플러스 플러스, 혹은 줄여서 시플플이라고 읽는다. 한국에서는 사실상 속어에 가까운 씨쁠쁠로 불린다.(...) '씨뿔뿔'로 발음되는 경우도 많다. 참고로 이와 비슷한 경우로 '뿔테 - 플테' 사례가 있는데, 이쪽은 민간어원 때문에 엄연히 '잘못' 발음되는 경우. 연세가 꽤 있으신 교수님 중에는 간혹 '시 더블 플러스'라고 읽는 경우도 있다. 미국에서는 그냥 CPP(시피피)라고 읽는다.

한국에서 컴퓨터공학과를 다닐 경우 배울 수 있는 프로그래밍 언어 3개 중에 속한다. 나머지 둘은 CJava. 그러나 실상 C++11 이후의 모던 C++ 기능을 가르쳐주는 곳이 매우 드물어서 C++의 객체 지향만 조금 배울 수 있다. C++의 객체 지향은 다른 언어에 비해서 좀 더 복잡하다. 제대로 다루기 위해서는 별도의 서적을 참고하는 것이 권장되며, 실제 프로젝트의 코드에 대해 탐구하고 경험해보는 것이 좋다.

2.1. C와의 차이점

C++의 개발이 C언어를 기반으로 이루어졌기 때문에 대부분의 C 프로그램은 C++ 컴파일러에서도 문제없이 컴파일된다. 초기 C++ 컴파일러는 일단 C++ 코드를 C로 변환하고 그걸 C로 컴파일하는 방식을 사용했을 정도. 다만, C가 항상 C++의 부분집합이었던 것은 아니라서 공통적인 부분에서 몇몇 차이점이 있으며[5], 순수 C 소스 코드를 C++로 컴파일 할 때 문제의 여지가 생길 가능성이 있다.

C++는 C와 프로그래밍 패러다임이 크게 다르다. 그래서 C로 작성된 프로그램에서 C++ 방식으로 코딩하려면 해당 코드에서 C++에 새로 도입된 것을 추가하는 게 아니라, 설계부터 시작해서 완전히 새로 해야 하는 경우가 많다. rm -rf / C++의 객체지향이 다른 객체지향 언어에 비해 이해하기가 만만한 개념이 아닌 데다가 C++의 객체지향은 C언어를 하위 요소로서(항상 그래온 것은 아니지만) 유지하면서 여러 문법 요소를 추가했기 때문에 신경 써야 할 부분이 많다. 다른 객체지향 언어에서보다 잘 다루는데 더 많은 공부가 필요하다. 따라서, 초심자 입장에서는 C와 C++는 완전히 다른 언어로 파악하고 접근하는 것이 좋다. [6]

C++ → C 테크도 만만치가 않다. C → C++는 절차지향 언어의 사고방식, C++ → C는 객체지향 언어의 사고방식이 머릿속에 굳어버리기 때문이다. C++가 C의 모든 기능을 포괄하고 있으므로 C++를 할 줄 알면 C도 할 줄 안다고 생각하기 쉽지만, 사실 C++가 명시적으로 비교적 간단히 사용할 수 있도록 제공하는 기능들을 C에서는 암묵적으로 여러 가지 수많은 '트릭'을 통해서 쥐어짜내듯이 만들어 사용하는 경우가 많다. GNOME의 핵심 라이브러리인 Glib이 대표적으로 C 언어로 객체지향을 짜도록 구성되어 있다. incomplete type declaration 트릭을 이용하여 캡슐화를 흉내내고, 매크로와 컴파일러 확장을 이용해서 type-generic function을 만들어 쓰고, struct hack을 이용하여 vector를 흉내 내는 식이다. 게다가, 저런 트릭은 언어 차원에서 정식으로 제공한다기보다 말 그대로 '트릭'에 가까우므로 이상한 조건들이 붙는 경우가 종종 있고, 그것들을 정확히 파악하고 있지 않으면 상당히 찾아내기 어려운 에러를 내는 경우도 많다.

그래서 많은 대학에서 컴공 1학년 1학기 때 C를 먼저 가르치고 빠르면 2학기, 늦어도 2학년에 C++를 가르치지만, 교수가 절차지향이 머리에 굳어버린다며 컴공에서 C 대신 C++와 객체를 먼저 가르치고 C는 아예 건드리지도 않고 다른 하드웨어 관련 학과에서만 가르치는 대학들도 많다. 객체지향만 배운다면 Java부터 시작하는 게 낫지만, 포인터 등을 통해 메모리와 각종 저수준(low level) 프로그래밍과 객체지향을 함께 배울 수 있기 때문이다. 컴공의 경우에는 C++를 모르면 2학년부터는 강의를 전혀 이해할 수 없기 때문에 의외로 많은 학생들이 중도포기를 하게 되며 컴공을 나왔는데도 프로그래밍을 전혀 못하는 학생들이 수두룩하게 발생하고 있다.

C를 알고 있다고 C++를 쉽게 할 수 있는 것은 아니다. 두 언어는 추구하는 설계 구조가 상당히 다르기 때문이다. C언어는 절차 지향이지만 C++는 절차 지향, 객체 지향, 일반화 프로그래밍, 함수형 프로그래밍을 모두 지원하는 언어다. 그래서 C언어를 알고 있는 사람이 C++ 초보자용 교재를 1권 끝내고 프로그램을 만들어 보라고 해도 대부분 C 방식에서 벗어나지 못한다. C++ 방식을 잘 활용하려면 STL까지 배운 다음 자료구조와 스트림, 템플릿 정도는 쓸 수 있어야 한다.

한편, C, C++ 중 어디가 쉽다라던가(...) 하는 건 어디까지나 간단한 프로그램의 예일 뿐이다. 둘의 설계 구조가 다르고 성능상 미묘한 차이밖에 안 난다. C의 함수구조에서는 다중의 인자값 전달이 필수적인데 구조체에 전부 때려박더라도 전역변수가 아닌 이상 적어도 1개는 전달해야 한다. 반면에 C++ 측은 클래스의 this를 활용하면 인자 전달 없이 클래스에 속한 멤버함수 호출만 할 수 있다. 헤더참조 구조가 중복정의로 복잡해지는 건 덤. Node.js 모듈구조를 참고 해 보면 module.exports로만 다른 모듈과 상호작용 할 수 있는데 C 계열은 헤더를 넣으면 그 안에 있는 모든 건 상호작용되고 원치 않으면 소스파일과 분리해야 한다. 다만, C++의 경우 C++20 개정판부터 모듈 개념이 도입될 예정이므로 이러한 단점은 점차 시간이 지남에 따라 어느 정도 극복될 것이다. 다른 모듈에서 정의된 매크로는 사용하지 못하기 때문에, 매크로로 떡칠된 코드는 다 뜯어 고쳐야 할 수도 있다. 이 때문에 수많은 기존 프로젝트는 여전히 헤더를 사용할 것으로 보이며, C와의 호환성을 위해 헤더 참조 구조는 표준에서 없어지지 않을 것이다.

C에서는 stdio.h#include 전처리기 구문으로 포함하지 않더라도 printf 등의 함수를 사용할 수 있지만, C++는 반드시 포함해야 사용할 수 있다.. 또한 C++98 초과의 최신판은 메인을 제외한 함수에 반환값의 자료명을 꼭 명시해 주어야 한다.

2.2. C++에서의 객체지향 프로그래밍

C++는 객체 지향 프로그래밍을 지원하지만, C++의 객체지향은 다른 객체지향 언어에서와는 성격이 좀 다르다. 대부분의 객체지향 언어에서는 많은 부분을 런타임에 처리하며 메모리를 자동으로 관리하는 반면, C++에서는 최대한 많은 것을 컴파일 타임에 처리하는 것을 지향하며 메모리 등을 프로그래머가 직접 관리하게 하기 때문에 전반적인 클래스 디자인 자체가 상당한 차이를 보이게 된다. Java 등의 다른 객체지향 언어에서와 같은 방식으로 C++ 클래스를 디자인하면 거의 틀림없이 컴파일이 제대로 안 되거나 메모리 문제가 발생한다. 특히 쓰레기 수집을 지원하는 Java에서 C++ 테크를 탄 학생이라면 처음에는 메모리가 줄줄 새는 프로그램을 만들게 될 것이다. 반면 C++ 스타일로 다른 객체지향 언어에서 프로그래밍을 하는 경우 특별히 안 될 것은 없지만, 해당 언어의 스타일로 작성한 코드에 비해 시간이 많이 걸리고 너저분해 보이며 클래스 구조가 경직되어 수정하기 어려워진다. 소멸자가 호출되지 않는 등의 차이점은 있지만 심각한 문제가 되는 경우는 많지 않다.

대표적인 예가 함수 내부에서 객체를 동적으로 생성해서 그 포인터를 반환하는 것. C++에서는 이는 메모리 문제를 일으키기 딱 맞는 방식이라서 클래스 디자인 단계에서 이런 종류의 함수가 필요없게 하기 위한 고려가 필요하다. 거의 대부분의 프로그램은 실행되고 있는 코드의 각 분기점에 대한 변수를 스택 영역에 저장하는데, 한 함수의 실행이 완료되면 그 함수의 스택 영역 변수를 위한 메모리는 모두 삭제된다. 그리고 이 변수는 쓰레기 데이터로 남아있다가 다른 함수를 호출할 때 덮어씌워진다. 해당 함수가 끝나더라도 공유 자원과 같은 특정 데이터를 유지하려면 힙 영역에 할당해야 한다. 그러나 가비지 컬렉션이 자동으로 되는 언어에서는 매우 흔히 사용되는 방법이다. 이런 차이 때문에, Java, C# 등의 언어를 먼저 접한 프로그래머들은 C++의 객체지향이 짝퉁이라며 싫어하기도 한다. 하지만 객체지향 프로그래밍 언어임을 전면에 내세우는 Java나 C#과 달리, 객체지향 패러다임 지원하는 멀티패러다임 언어 C++ 입장에서는 얼마나 순수하게 객체지향의 이상을 잘 따르는가보다 C++가 제공하는 다른 패러다임까지 아우르는 내적 일관성이 더 중요하므로 해당 언어들과 C++를 동일선상에 놓고 비교할 수는 없다.

이러한 차이가 생기는 것은 대부분의 객체지향 언어는 직접적으로 Smalltalk의 영향을 받은 반면, C++는 Smalltalk보다 먼저 객체지향의 초보적인 개념을 제시한 시뮬레이션 전용 언어인 Simula에서 직접 영향을 받았기 때문이다. 이는 C 프로그램과의 호환성을 고려한 결과이기도 하지만, 기본적으로 C++에서 프로그램의 성능을 희생시키지 않기 위해서였다. Smalltalk의 경우 당시의 기술적 한계도 있고 해서 C보다 대체로 수십 배 정도 느렸고, 이는 C++에서 지향하는 결과가 아니었기 때문이다.

다만 이런 제한이 생기는 건 오래된 스타일의 C++ 코드에서나 그런 것이고, Modern C++에서는 스마트 포인터를 사용하거나 이를 move[7]하는 방식으로 그들과 완전히 같은 문법이 구현 가능하기 때문에 더 이상 해당이 없는 사항이다. C++의 스마트 포인터는 예를 들어 한 동적 객체를 여러 소유권자가 공유하는 경우 (참조 대상에 대한 횟수를 기록하는) 레퍼런스 카운팅을 지원하는 std::shared_ptr를, 단일 소유자만 존재하는 경우 std::unique_ptr를 사용한다. 또한 레퍼런스 카운팅으로 인해 발생하는 std::shared_ptr의 순환 참조 문제를 피하기 위해서 참조만 할 뿐 소유권은 지니지 않는 std::weak_ptr을 사용한다. 더 정확히 말하자면 std::weak_ptr은 객체 대신 std::shared_ptr을 참조한다고 보면 된다. 하나의 std::shared_ptr과 다수의 std::weak_ptr을 사용하면 여러 소유권자가 하나의 객체를 공유하면서도 순환 참조 문제를 해결할 수 있다.

2.3. C++에서의 일반화 프로그래밍

C++에서는 템플릿을 이용한 일반화 프로그래밍(Generic Programming)이 매우 폭넓게 사용된다. 특히 C++11을 시작으로 하는 모던 C++는 일반화 프로그래밍을 빼고 이야기하는 것이 불가능하다. 당장 매우 널리 사용되는 std::string만해도 실제로 들여다보면 std::basic_string<char>와 같은 형태의 클래스 템플릿의 특수화에 지나지 않는다. 또, C와는 달리 포인터 배열 대신에 std::vector를 기본으로 사용하라고 가르치는데, 이 std::vector 또한 저장될 변수의 타입을 파라미터로 받아들이는 클래스 템플릿이다. (예를 들어 std::vector<int>) 정적 배열이 꼭 필요할 경우 std::array를 쓴다.

일반화 프로그래밍의 결과물으로는 C++ 표준 라이브러리의 일부분으로 포함된 컨테이너, STL 같은 것들이 있으므로, C++ 표준 라이브러리를 사용하는 순간 일반화 프로그래밍의 도움을 받는 것이다. 따라서 '나는 C++를 사용하지만 템플릿을 이용한 일반화 프로그래밍은 어려우니까 패스하겠다'는 말은 애초에 성립하지 않는다.

중급 이하 개발자는 이런 결과물을 일상적으로 쉽게 가져다 쓰면 되고, 직접 만들어 쓸 필요는 없다. 이는 중급 개발자 정도로는 템플릿을 사용한 일반화 프로그래밍 기법을 정확하게 적용하는 것이 무척 까다롭고, 디버깅할 때 이해 불가능한 컴파일러 메세지를 받게 되는 경우가 많아서 발견된 오류를 수정하기도 어렵기 때문이다.

이런 이유로 C++ 학습 초반에 템플릿 프로그래밍을 직접 하는 것을 피하고 기껏해야 컨테이너 클래스만 사용하는 습관이 들다 보니, C++는 객체지향 언어이고 STL이라는 템플릿 라이브러리를 덤으로 쓸 수 있는 정도라는 오해가 널리 퍼진 것이라 생각된다. 하지만, 특수 목적의 컨테이너를 설계하거나 범용 라이브러리를 설계하는 수준의 고급 개발자가 되려면 템플릿을 사용한 일반화 프로그래밍을 해야 한다.

JavaC#에서 찾아볼 수 있는 Generics가 지금 설명한 C++ 일반화 프로그래밍의 아주 제한된 형태의 적용례에 해당한다.

2.3.1. 템플릿 메타 프로그래밍

TMP, Template Meta Programming

템플릿 문법을 응용해서 컴파일 시점에 자동 코드 생성 및 계산 같은 것을 한다는 개념이다. 영문위키백과 배우기 어렵고 알아보기도 힘들고, 오류 찾기도 힘들다. 당연히 C++의 템플릿을 모를 경우에는 아예 이해할 수가 없는 개념이기도 하다. 하지만 알아두면 은근 써먹을 데가 많다.

까딱 잘못하면 바이너리 크기가 n배로 증가하는 참사가 발생할 수도 있다. 이럴 경우 명령어 캐시의 실패 확률이 늘어나서 오히려 성능이 하락할 수도 있다. 하지만 제대로 구현하면 바이너리 크기에서 손해를 보지 않으면서도 컴파일 시점에 다양한 처리나 계산을 할 수 있기에 라이브러리 구현에 종종 이용된다.

그리고 처음에는 너무 병맛이라 입에서 욕이 튀어나올 수 있다. 악용에 가까운 테크닉으로는 다음이 있다.

  • SFINAE(Substitution Failure Is Not An Error): 함수를 오버로딩 하는데 있어 조건에 따라 일부러 문법적으로 틀린 템플릿의 구현 코드를 발생시켜 틀리지 않은 문법이 되는 특정 오버로드만 선택되게 만드는 테크닉이다. C++ 문법의 틈새에 존재하는 문법 오류이지만 컴파일 에러가 아닌 상황을 적극 활용하는 것이다. 직역하면 '대입 실패가 에러라는 뜻은 아니다'라는 뜻이다.
  • Expression Templates: 디자인 패턴의 일종인 Proxy pattern 기반의 Lazy evaluation이 적용되는 효율적인 계산 코드를 컴파일 시점에 자동으로 생성하는 테크닉. 일반적으로 연산 도중의 임시 객체 생성 문제를 이 기법을 통해 해결하는 경우가 있다. RVO(Return Value Optimization)를 감안하더라도 C++ 특성상 연산자를 활용하는 과정에서, 직접적인 연산을 시도하면 임시 객체의 생성을 완전히 막을 수는 없기 때문이다.

이 개념은 C++ 템플릿이 코드를 컴파일 시점에 확장하는 방식이라는 것과 비타입 인수도 줄 수 있다는 것을 이용한다는 점을 이용악용하는 것으로 다른 언어에서는 비슷한 것도 찾기 힘들다. 전술한대로 JavaC#에 있는 제네릭은 TMP가 아닌 일반화 프로그래밍(Generic Programming)의 개념이다.

같은 템플릿 문법을 사용하지만 앞 문단에서 강조한 일반화 프로그래밍과는 엄연히 목표가 다르다. 일반화 프로그래밍이 코드의 범용성 증대를 목표로 한다면 템플릿 메타 프로그래밍은 컴파일러를 고문해 어떻게든 실행시간의 이득을 얻은 것을 목표로 한다. 근본적으로 C++의 템플릿 문법이 compile-time turing complete하기 때문에 이런 일이 가능한 것이다. C++ 안에 컴파일 타임 전용의 또 다른 언어가 숨어있는 것과 같은 상황이다.

2.4. 성능

C는 유닉스라는 OS를 만들기 위해 어셈블리 대체용으로 만들어진 언어라, 머신 컨트롤의 저수준 작업이 주된 임무 중 하나였다. 그리고, C++은 그 이름에서 보이듯이 사실상 C를 대체하기 위한 언어였기 때문에 C가 제공하던 머신 컨트롤의 저수준 작업들을 포기하지 않고 그대로 지원한다.

거기다 그 위에 여러 가지 추상적인 고수준 기능들을 집어넣어 추상적인 시스템 위에서만 노는 게 아니라, 저수준의 기계 제어까지 가능한 추상화라는 요상한 철학을 지지한다. 그래서, Java 같은 언어들이 객체 지향 프로그래밍을 구현하면서 동적 바인딩(C++의 가상 함수)을 디폴트로 쓰고 쓰레기 수집을 지원할 때 C++은 정적 바인딩을 디폴트로 하고 수동 메모리 관리를 유지한 것이다. 일반적으로 추상화 수준이 높은 언어일수록 프로그래머 머리로 해야할 구체적인 것들을 컴퓨터가 대신 해주고, 이것이 항상 최적화된 방식은 아니기 때문에 프로그램의 실행 속도가 상대적으로 느리게 마련이지만, C++ 프로그램은 위와 같은 이유로 성능 하락이 거의 없다.

추상화의 가장 큰 이유와 장점은 그것을 추상화시킴으로서 그 아래 감춰진 디테일한 부분을 신경쓸 필요가 없게 만드는 것이다. 따라서 저수준 제어와 고수준 추상화의 두 가지 개념은 서로 완전히 충돌하는 부분이다. C++는 저수준 제어를 포기하지 않았기 때문에 프로그래머는 C++로 저수준 작업을 할 때는 디테일한 부분까지 신경써야만 한다. 반대로 C++에서 고수준 기능을 사용하려면 프로그래머가 저런 부분을 감안하여 C++ 방식으로 변형되어 적용된 코딩 문화(RAII[8] 등)를 알고 있어야 한다.

템플릿이 사용된 모든 타입에 대해 컴파일 시간에 확장하도록 설계된 이유 또한 실행 성능을 포기하지 않기 위해서이다. C++과 비슷한 정도의 기능을 가진 언어 중에서 C++만큼 빠른 실행 성능을 내는 언어 구현은 흔치 않다. 대신 프로그래머가 언어의 이상한 부분까지 신경쓰지 않으면 안 되는 디자인이 되어 제대로 쓰는 게 굉장히 어려운 언어가 되고 말았다. 뭔가 딜레마 같지만 C++과 비슷한 정도의 고수준 기능을 제공하는 언어는 절대로 C++만큼 복잡하지 않다. 덤으로, 템플릿 같은 경우엔 실행 성능을 포기하지 않는 대신 컴파일 시간을 심각하게 포기했다. 심지어 디버깅도 힘들다. 다만 디버그 문제는 자기가 원하는 템플릿에 맞춰 중단점을 거는 게 가능해지는 등 많이 개선된 편이다.

애초에 C 언어의 기능을 모두 포함했던 이유는 생산성과 함께 C를 대체하겠다는 두마리의 토끼가 목적이었기 때문이다. 그러나, OS를 만드는 유닉스 커뮤니티들이 C++로의 전환을 거부해서 한마리 토끼는 놓쳤고... 그럼에도 Java라는 강력한 언어가 등장하기 전까지는 C에 비해 압도적인 생산성으로 어플리케이션 소프트웨어 쪽에서 순식간에 대세언어가 되었으나...Java의 등장과 각종 스크립트 언어들의 부상, 그리고 비주얼 베이직에서 Python으로 이어지는 하이퍼 고 생산성 언어들의 등쌀에 현재는 포지션이 좀 애매해진 경향이 있다. 애초에 C의 기능을 전부 포함했던게 공짜로 된 것이 아니라 그만큼 복잡해지고, 컴파일 속도까지 희생하는 역효과도 포함하고 있었고, 덕분에 Java나 C# 등의 기타 고생산성 언어들에 비해 생산성은 떨어지면서 정작 노리고 있던 C의 대체도 날아간 상황.

현재는 클라이언트 쪽에서는 퍼포먼스가 엄청나게 중요하면서 동시에 개발속도도 크리티컬한 게임[9][10], 포토샵, 웹 브라우저 등의 데스크탑 어플리케이션, 업무 연속성이 중요한 금융권 IT망 및 금융공학에서 주로 사용된다. 사실 그 외에도 퍼포먼스를 중시하는 경우 여전히 많이 사용된다. 구글에서도 상당히 많이 사용하는 언어이다.# 이미 구글에서 2009년에 개발한 컴파일 기반 언어인 Go가 나와 있었는데도 텐서플로우가 C++로 개발되었다는 점이 하나의 예시이다.

2.5. 단점

2.5.1. 난이도

선요약을 하자면 기능이 다양하고, 성능이 프로그래밍 언어 중 손에 꼽힐 정도로 좋지만 그만큼 양이 방대하고 배우기가 어렵다. 그리고 방대한 기능이나 특성을 잘 활용하는 것도 어렵다. 그래서 중도 포기하기 쉽다. 또한 C++11이 발표된 2011년을 기점으로 매 3년마다 개정된 표준을 발표하는 것이 관례화 되면서, 기능과 성능은 더 늘어나거나 개선되었지만 그만큼 배워야 할 것은 더욱 늘어나게 되었다. JavaC언어에 비해 C++는 변화의 폭이 제법 큰 편이기 때문이다.

C++을 배우려는 초보자가 인터넷에 문의를 해보면 보통 초보자용 1개, 중급자용 1개, STL 1개 등등등 해서 여러 개 읽으라는 답변들이 많이 달리며, 개중에는 1000쪽이 넘어가는 책들도 있다.[11] 물론 반드시 1000쪽짜리 책으로 공부해야 한다는 건 아니지만 C++ 교재들은 대체로 다른 언어 교재에 비해 두꺼운 편이다.

C++는 자유도가 높은 언어인만큼 기능도 많고 다른 언어에 비해 구조나 설계에 제한이 적지만, 실수하기 쉽고 설계가 어렵다는 단점이 있다. 다른 언어도 마찬가지이지만 특히 C++는 교재를 꼼꼼히 공부한다고 해도 한참 부족하다. Boost와 같이 잘 설계된 라이브러리나 여러 소프트웨어의 소스 코드를 참고하고, 직접 소프트웨어를 설계하고 제작하여 실무 경험을 쌓는 것이 실력을 향상시키는데 있어 매우 중요하다.

C++11 이후 점차 추가되어가는 표준 라이브러리 기능들을 사용한 코드를 보면 당최 C와 기반이 같은 언어라고는 생각이 들지 않을 정도로 차이가 크다. 이는 3년마다 타 언어의 장점을 흡수하고 단점을 개선하면서 더더욱 심화될 수 있는데, 기존의 C++의 스타일과 많이 달라지고 있기 때문에 잘 알아둘 필요가 있다. C++98을 잘 알고 있다는 가정 하에, 모던 C++의 새로운 기능을 배우고 싶다면 이들을 자세히 구글링하여 찾고 부족한 내용은 C++ 표준 라이브러리를 정리한 사이트에서 알아보는 것이, 가장 최신의 C++ 기능들을 빠르게 익힐 수 있는 방법이다.

C++에서 할 수 있는 것을 다 할 수 있으면서 좀 더 쉽게 다룰 수 있도록 C++을 리엔지니어링해서 만든 D언어도 있지만, 인지도가 적고 퍼포먼스를 위해 GC(쓰레기 수집)를 사용하지 않을 때 제한되는 사항 때문에 성장이 정체되어 있다.

2010년에는 모질라 재단이 만든 Rust 언어가 발표되고 2015년에 정식판이 나왔는데, TIOBE 검색 순위상으로는 D언어보다도 떨어지지만, 2017년 Stack Overflow 사이트의 설문조사에서 가장 좋아하는 언어 1위를 달성했다. C언어와 같거나 빠른 속도를 목표로 하여 GC를 배제했고, 메모리 안정성을 최우선시하여 객체의 수명 관리를 컴파일 타임에 모두 결정할 수 있도록[12]설계되었기 때문에 C++의 실질적인 대안이 될 수 있는 비전 있는 언어로 평가받고 있지만, 사용할 수 있는 안정적인 라이브러리가 많이 부족하다. 게다가 기존 소프트웨어 인프라는 이미 다른 언어가 차지하고 있어서, 소프트웨어 시장 내 수요가 적으며 전문 인력을 구하기 어렵다. C++도 2011년 이후로 3년마다 새 표준이 나오며 개정되고 있기 때문에, C++를 대체하기까지는 오랜 시간이 걸릴 것으로 보인다.

2.5.2. 애매한 위치

리눅스 진영의 주축인 리누스 토르발스가 메일링 리스트를 통해 언급한 2007년도 비판이 자주 인용된다. 리누스 토르발스에 의하면 C++는 어중간한 지점에 위치한 언어라고 한다. 프로토타이핑 용도나 단순한 GUI 프로그래밍 용도로 사용하기에는 C++가 일을 충분히 쉽게 하도록 도와주는 친절한 언어가 아니고, 반대로 단순하고 직접적인 구조의 프로그래밍을 장려하는 C언어처럼 날씬한 운영 체제 프로그래밍용 언어도 아니라는 것이다.# 다만 리누스 토르발스가 OS 개발자인 만큼 C를 선호할 수밖에 없다는 점과 해당 발언이 모던 C++ 이전의 비판임을 참고해서 이해해야 한다. 그리고 C++가 제공하는 기능들이 워낙 막강한지라, 리눅스 진영에서도 커다란 프로젝트에는 C++를 상당히 애용하고 있다.

하지만, 아무리 C++이 예전과는 다르게 3년마다 표준을 내놓으면서 발전한다 해도 점유율은 생산성이 높은 다른 언어나 도구에 밀려 꾸준히 떨어지고 있다. 다만, Tensorflow이나 게임, 드라이버, 임베디드가 C++로 개발되고 있는 것과 마찬가지로 성능이나 자원 절약이 최우선시되는 경우 C/C++가 유일한 선택지이기 때문에 많은 프로젝트에서 여전히 사용하고 있다.

2.5.3. 성능 저하 요소

C++의 고성능에 대한 철학은 흔히 "don't pay for what you don't use", 즉 성능에 군더더기 없는 기능으로 대표된다. 하지만 두 가지 언어 요소가 이를 정면으로 어기는 기능으로 여겨지는데, 하나는 RTTI(Run-Time Type Information)이고, 다른 하나가 바로 예외(Exception) 이다.

2.5.3.1. RTTI

RTTI(Run-Time Type Information)는 컴파일 타임이 아니라 런타임에 객체의 타입이 결정되는 C++의 매커니즘이다. 프로그램이 실행될 때, 객체에 대한 정보를 저장하고 확인해야 하기 때문에 오버헤드가 발생한다. 다음 문법이 대표적인 예이다.

- dynamic_cast- virtual

dynamic_cast의 경우 상속 트리 전체를 휘젓고 다녀야 한다는 점 역시 큰 성능 문제로 꼽힌다. 하지만 이에 의존하는 언어의 기능이 거의 없기 때문에 대부분의 상황에서 성능 패널티를 피할 수 있다. 메이저 컴파일러에서 아예 기능을 제외하는 옵션을 제공하는 건 덤이다.

virtual의 경우 50% 정도의 성능 하락이 있을 수 있지만, 현대 컴퓨터에서는 분기 예측을 잘 활용하여 평균 5~10% 정도의 성능 하락을 보여준다. 그리고 이러한 성능 하락은 어디까지나 함수 호출 비용으로만 계산한 것이기 때문에 특수한 상황이 아니라면 고민하지 않아도 된다. 많은 소프트웨어가 이 문법을 자주 사용한다.

2.5.3.2. 예외

예외도 마찬가지로, 설계상 예외 핸들러를 찾기 위해 (RTTI와 동일한 맥락으로) 던져지는 예외의 타입별로 타입 캐스팅을 해야 하기 때문에 런타임 타입 정보가 필요하다.

따라서 예외를 던질 때마다 적지 않은 성능 패널티가 부과된다. 심지어 예전에는 try 블럭을 설치하는 것과, 소멸자가 있는 오브젝트를 정의하는 것만으로도 프로그램 성능이 크게 하락했었다. 최신 컴파일러는 zero-cost exception[13]을 사용하기 때문에 성능 하락이 거의 없다. 현대 컴파일러에서 if문 처리는 분기 예측에 따라 1~20 cpu cycle를 소모하고, 예외처리는 대략 5000 cpu cycle를 소모한다#는 점을 고려하여, 불필요한 예외는 사용하지 말아야 하며 성능이 중요한 부분에서만 예외 처리를 사용하지 않는 것을 추천한다.[14]#

RTTI와 달리 C++의 표준 라이브러리를 사용하고 에러 처리를 동반하는 한 예외는 현실적으로 피할 수 없다. C++에서 객체의 생성이나 복사를 실패시킬 수 있는 문법적 기능은 예외가 유일하며[15] 표준 라이브러리는 이를 적극적으로 활용하기 때문이다.[16]

에러 처리 수단은 수많은 프로그래밍 언어의 핵심 매커니즘이다. 하지만 이런 상당한 단점이 있게 되자, 대규모 프로젝트에서는 성능 상의 이유로 예외의 사용을 금지하게 되고[17], 오히려 C에서 흔히 쓰는 에러코드 등의 매커니즘으로 회귀하게 되었다. 심지어 에러 처리를 일관적으로 예외로 하던 표준마저도 어느 정도 스탠스를 바꾸어, C++17의 filesystem 라이브러리의 경우 예외 처리와 에러코드용 인터페이스를 혼용해서 제공해버릴 정도로 C++의 예외 처리는 처리 비용이 상당하다.

이렇게 성능 상의 문제로 예외 처리를 기피하는 상황이 발생하자, 아예 에러코드 방식을 확장시킨 Boost.Outcome 혹은 std::expected[18] 등으로 발전하려고 하고 있다. 타입 캐스팅을 포기하고 에러코드와 예외의 장점을 섞은 표준안이 제안되고 있으므로 #P0709 언젠간 개선되리라는 희망을 가질 수는 있겠지만, 아직도 많은 논의가 계속되고 있기 때문에# 당분간은 적용이 요원하다.

3. 역사

3.1. 클래식 C++

3.1.1. C with Classes (1979년)

1979년에 비아르네 스트로우스트루프가 속도가 느리지만 객체 지향적인 언어인 Simula의 컨셉을 C언어에 접목시키면서 C with Classes라는 이름으로 만들었다.

3.1.2. C++ (1983년)

1983년에 가상 함수, 연산자 오버로딩, 참조, const, new/delete 연산자 등을 포함한 새로운 기능들이 추가되면서 언어 이름도 C with Classes에서 C++로 변경되었다. 이 시점에서 이미 클래식 C++의 틀은 완전히 잡혔다고 할 수 있으며, 이때까지는 거의 스트로우스트루프가 언어 개발을 전담하다시피 했다.

1985년에 본인이 쓴 책인 The C++ Programming Language를 통해 C++라는 언어를 정의하여 레퍼런스 컴파일러를 배포하는 식으로 개발되었다. 한편, 컴파일러 쪽에서는 백엔드는 여전히 C 컴파일러이지만 프론트엔드만 Cfront라는 이름으로 새로 개발되었다.

3.1.3. C++ 2.0 (1989년)

1989년에 다중 상속, 추상 클래스, static 멤버 함수, const 멤버 함수 및 protected 멤버 등의 새로운 기능들이 추가된 C++ 2.0이 발표되었으며, 컴파일러의 프론트엔드도 Cfront 2.0으로 업그레이드되었다.

1990년에 C++ 2.0에 추가된 기능 외에 템플릿, 예외, 네임스페이스, new casts 및 bool 타입 등의 새로운 기능들이 추가된 '주석이 달린 C++ 레퍼런스 메뉴얼'이 발표되었고 1991년에 컴파일러 프론트엔드가 Cfront 3.0으로 업그레이드되었으며, The C++ Programming Language의 첫 번째 개정판이 발행되었다.

3.1.4. C++98 (1998년)

1998년에 최종 승인된 C++의 첫 공식 표준안으로, C++ 2.0 이후 C++가 안정적으로 정착하고 수요가 폭증함에 따라 여러 회사에서 상업용으로 컴파일러를 만들었고, 이에 따라 코드가 컴파일러에 따라 중구난방이 되는 것을 막기 위해 표준화의 필요성이 제기된다. 그래서 1990년에 ANSI에서 C++ 표준화를 위한 위원회가 설립되었으나 1991년에 ISO로 넘기면서 스트로우스트루프를 비롯한 프로그래밍 언어학자 및 컴파일러 제조사들을 모아 표준화 작업을 하기 시작한다.[19] 그렇게 C++의 표준안이 ISO/IEC 14882:1998라는 공식 명칭으로 처음 제정되었으며, 이를 줄여 C++98이라 부른다.

3.1.5. C++03 (2003년)

2003년에 C++98 문서의 애매모호했던 부분들을 보완한 C++03이 ISO/IEC 14882:2003라는 정식 명칭으로 제정되었는데, 이는 그냥 표준화 문서상 불명확했던 것을 교정한 버전이다. 예를 들어 std::vector가 배열처럼 연속된 메모리를 차지하도록 표준에 명시했다. 사실 당시 컴파일러들 대다수가 이미 C++03 제정 이전부터 이러한 부분들을 잘 구현한지라, 실제 프로그래머 입장에서 변한 건 없다.

3.2. 모던 C++

각종 주요 컴파일러들과 모던 C++의 호환성은 여기서 볼 수 있다.

3.2.1. C++TR1 (2007년)

2005년 6월 24일에 표준 라이브러리를 확장하는 것을 골자로 하는 C++ 테크니컬 리포트의 초안이 작성되어 2007년 11월에 제정되었다.

첫 번째 테크니컬 리포트라고 해서 C++TR1으로 알려져 있는데 혹자는 이를 표준안과 동급으로 간주하고 C++07/TR1이라 부르기도 한다. 정식 명칭은 ISO/IEC TR 19768:2007이며, C++ 표준화 이후 사실상 언어에 변화를 가한 첫 번째 사양이지만 엄밀하게 표준안은 아니라서 컴파일러 제작사가 원하면 넣고 아니면 마는 수준의 선택적인 확장안이었다. 그래서인지 전부 std 네임스페이스에 들어가 있지 않고 대신 std::tr1 네임스페이스로 분리되어 들어간다. 이후 C++TR1의 기능들은 대부분 C++11 표준 사양으로 흡수된다.

C++TR1 초안이 발표되기 2달 전인 2005년 4월 29일에 C++TR2의 초안에 대한 작업을 시작했었다. 여기에는 유니코드, XML/HTML, 네트워킹, 파일 시스템 등의 기능이 포함될 예정이었으나, 이는 무산되었다. 대신, 각각의 라이브러리와 기능들을 "Technical Specifications"이라는 별도의 비표준 기술 사양으로 별도로 제정하기 시작했다. 여기에는 파일 시스템, 병렬 컴퓨팅, 트랜잭셔널 메모리, 컨셉트, Range, 코루틴, 네트워킹, 모듈, 리플렉션 그리고 심지어 그래픽까지 있지만, 이러한 것들이 추후 표준에 포함될 지 또는 얼마나 수정될 지는 미정이다. C++TR2의 일부 사양이 C++11에 포함되었으며, C++17, C++20에 해당하는 기능들까지 포함되어 있는 등 실험적인 성격의 사양임을 짐작할 수 있다.

이러한 시범 사양은 std::experimental이라는 네임스페이스에 포함되어, MSVCLLVM/Clang에서 일부 지원한다. 시범적인 사양은 이 사이트에서 확인할 수 있다.

3.2.2. C++11 (2011년)

새로운 기능들이 많이 포함되었던 C++TR1이 정식 표준으로 채택되지 못 해서 대부분의 프로그래머들에게 있어서 C++은 C++98 이후 13년 간 정체된 언어나 다름 없었다.

2011년 8월 12일에 최신 컴퓨팅 환경을 지원하기 위해 급격한 변화의 첫 단추를 끊은 새로운 표준안인 C++11이 ISO/IEC 14882:2011라는 정식 명칭으로 최종 승인되었다. 최종 승인되기 전까지 표준안 개발 당시엔 C++0x라 불리었는데, 2000년대 안에는 표준화가 될 것이라는 의미였다. 하지만 결국 2000년대가 끝나고 2011년 8월에서야 정식 C++ 표준이 되어서 11이 붙었다. # 여담이지만 추가된 많은 기능 중 상당수 항목들이 boost 라이브러리를 다수 참조[20]했다. C++11에서 r-value/move[21], constexpr, std::initializer_list, Range-based for loop[22], lambda expression, 스마트 포인터[23], 정규표현식[24], 멀티쓰레드 등등 중요한 신 개념과 편의 기능들이 많이 추가되었으므로 반드시 확인할 필요가 있다. C++11에 추가된 내용만 적절히 활용해도 C++ 코드의 작성과 관리가 보다 명확하고 더 쉬워진다.

C++ 표준 위원회는 C++98에서 C++11까지 업데이트가 나오는 데 시간이 매우 오래 걸린 것이 업데이트가 잘 완성된 후에 발표하려고 했던 접근 방식 때문이었다고 자평한 뒤, 이러한 완벽주의를 버리고 3년에 한 번씩 그때까지 정리된 업데이트만 발표하는 형식(기차 모델)으로 규칙적인 업데이트를 하는 것이 훨씬 생산적인 접근방식이라고 결정하였다.

3.2.3. C++14 (2014년)

2014년 8월에 ISO/IEC 14882:2014라는 정식 명칭으로 C++14이 제정되었다. 표준안 작성 당시 C++0x라 불리었던 C++11처럼 C++14도 C++1y라는 코드네임으로 불렸으며, 주로 C++11에 추가된 요소들을 다듬고 확장하는 데 치중한 비교적 마이너 표준안이다.

3.2.4. C++17 (2017년)

2017년 12월에 C++1z로 알려졌던 C++17 표준이 ISO/IEC 14882:2017이라는 정식 명칭으로 최종 승인되었다.

C++11의 추가 요소들을 다듬고 향상시키는데 주력했던 C++14에 비해, C++17은 표준 라이브러리의 새로운 기능에 비교적 주력한다. 초안 단계에서부터 논란이 많은 표준안이기도 하다. C++11/14의 실정에 맞추어 옛날 기능을 삭제하자는 안이 많기 때문인데[25], deprecated 판정을 받은 std::auto_ptr이나 삼중자(trigraph)[26] 등이 삭제되었다. 이는 옛 코드에 대한 호환성을 포기하는 것이기 때문에, 오래된 코드들이 최신 컴파일러에서 컴파일되지 않을 가능성이 크다. 이 때문에 IBM에서 문자체계의 문제를 내세워 삼중자를 제거하지 말라고 회사 차원에서 표준화 위원회에 진정서를 제출했었다. #

C++17에는 파일 시스템, STL 병렬 알고리즘 처리[27] 등이 추가되었다. 그외의 새로운 기능들은 이곳에 잘 정리되어 있다. 예를 들면 C++17에 추가된 structured binding을 잘 활용하면 두 개 이상의 값을 반환하는 기능을 이용할 수 있다.

3.2.5. C++20 (2020년)

2020년에 최종 승인된 C++20은 C++11, C++14, C++17에 이은 주요 버전이다. 비동기 프로그래밍을 지원하기 위한 코루틴[28], 컴파일 속도 향상을 위한 소스 파일 모듈화[29], 템플릿 매개변수에 조건을 추가하기 위한 컨셉트[30]다양한 기능들이 추가될 예정이다. 그 외에 추후 표준에[31] 추가될 수 있는 리플렉션과 네트워크 라이브러리, 2D GUI 라이브러리, 트랜잭셔널 메모리에 대한 시범 사양도 별도로 공개되어 있다.

더 놀라운 점은 C++20에 추가된 기능들과 C++20에 다 반영되지 못한 대신 다음 C++ 표준에 포함될 예정인 기능들이 대부분 C++TR2 제안서에 이미 언급되어 있던 사양이라는 특징이 있다. 표준화로써 최소 15년만에 빛을 발하게 된 셈.

4. 방언 및 확장

4.1. C++/CLI

2005년 비주얼 스튜디오 2005에 도입된 .NET Framework의 CLR에 맞물려 동작하는 C++. 보통 프레임워크 정도로 제공되는 다른 라이브러리와 달리 .NET의 메모리 구조에 맞추어 언어 자체가 다르다.

2002년에 비주얼 스튜디오 .NET 2002에 도입되었던 Managed Extensions for C++[32]는 정말 못 써먹을 언어였기 때문에 Managed C++을 대체하기 위한 언어로 나왔으며, 적어도 스펙상으로는 게임을 만들기에도[33][34] 부족함이 없는 고속의 언어임에도 생산성은 C++에 비해서 높다고 한다. 특히 C#과 C++를 이어주는 래퍼 등의 모듈을 만들기에는 아주 제격이며, C/C++로 만들어진 프로젝트를 시간적 여유를 두고 닷넷 기반으로 바꾸는 용도로도 많이 쓰이고 있다. 필요에 따라 단계적으로 모듈을 바꿔치기 하다보면 어느새 순수 닷넷 애플리케이션 완성. 그러나 지원 및 이식성이 떨어져서 업계 표준으로 쓰기에는 무리다.

C++/CLI 코드 내에서는 관리와 비관리 클래스를 혼용할 수 있으므로 래핑 없이 모든 모듈을 C++/CLI로 작성해도 되지만, C++/CLI 내의 C++ 클래스는 순수 네이티브 라이브러리에서 만들어진 C++ 클래스에 비해 성능이 다소 떨어진다. 따라서 성능이 중요한 모듈은 별도의 네이티브 코드로 작성하고, C++/CLI는 이 모듈의 래핑을 담당하는 모듈만을 작성하는 것이 좋다.

여담이지만 .NET 출시 당시 마이크로소프트가 Managed 언어를 강력하게 밀어(C# 등) 기존에 쓰이던 Win32 API C++나 MFC는 찬밥 신세라는 인상을 줬다. 특히 닷넷 기술이 막 도입되던 비주얼 스튜디오 2005 당시 C++와 C#을 비교해 보면 대놓고 C++를 죽이려 한다는 느낌이 팍팍 들었을 정도. (하지만 MS가 자사의 핵심 코드를 .NET으로 바꾸거나 핵심 플랫폼 기능들을 열어주거나 한 케이스가 거의 없다.) VS 2008부터는 지원이 다시 강화되는 추세이며, C++11, 14, 17 지원 및 C++ AMP의 도입 등 여러가지 확장을 하고 있다. 물론 이것도 예전에 비해 나아졌다 수준이고, 인텔리센스나 코드 리팩토링 등의 기능을 보면 여전히 C#에 비해 지원이 미흡하다.

4.2. CUDA

2007년 6월에 발표된 NVIDIAGPU에서 동작하는 C++ 기반 GPGPU 언어.

4.3. OpenCL

2009년 8월에 발표된 개방형 GPGPU 언어. 원래는 C 기반이었으나 2015년 11월 16일에 발표된 OpenCL 2.1부터 C++14를 기반으로 문법이 확장되었다.

4.4. C++/CX

2012년에 WinRT 및 XAML을 지원하기 위해 마이크로소프트에서 고안한 확장 언어. 라이브러리는 Windows Runtime C++ Template Library(WRL)을 사용했으나 2015년 6월 23일에 발표된 C++/WinRT로 대체되었다.

5. 문법

  자세한 내용은 C++/문법 문서를 참고하십시오.

6. 도서

C++는 초기에 배울 내용이 무척 많고 최종 목표로 하는 어플리케이션의 종류에 따라서[35] 중상급 레벨에서 가는 길이 크게 갈리기 때문에, 어디에서 시작할 것인가가 꽤나 학습 방향에 중요하게 작용해서 초급자용 도서 추천이 매우 곤란하다. 아래의 도서들은 한국어 번역본이 존재하는 도서를 우선시하였다.

  • C++ 창시자 본인이 학부생 수업 교재로 사용할 목적으로 쓴 Programming: Principles and Practice using C++는 보통 컴퓨터 관련 학부 2학년에 존재하는 프로그래밍 방법론 등의 이름을 가진 특정 언어와는 무관한 수업의 교재이지만, C++ 입문서로도 꽤 유용하다.
  • 외국의 입문서라고 하면 Teach Yourself 시리즈나, Primer 시리즈 등이 가장 처음 볼 책으로 추천되곤 하지만 초급 서적임에도 불구하고 둘 다 매우 두꺼운 편이며, 예제나 서술 방식에 호불호가 크게 갈리는 책이다.
  • 입문용 서적으로는 C언어와 마찬가지로 C++ 기초 플러스를 추천한다.
  • C언어의 창시자가 쓴 The C Programming Language가 초급자들부터 봐야 하는 책으로 유명한 것에 비하여, 비아르네 스트로우스트루프의 The C++ Programming Language는 절대 초급용 서적이 아닌, 중급 이후의 레퍼런스 서적이다.
  • 일단 초급용 서적을 한 권 독파하고 나면, Effective Modern C++, C++ Template: The Complete Guide, C++ Concurrency in Action 정도의 책을 권하는 사람이 많다. 특히 스콧 마이어스의 Effective Modern C++는 거의 필독서에 가깝다.[36]
  • 오래된(2005년) 책이지만 Effctive C++(3판)을 보는 것도 좋다. 번역이 매끄러워 읽기 좋고 이해하기 쉽다.
  • 인터넷으로 배우고 싶다면 http://soen.kr이나 모두의 코드를 추천한다.
  • 이 단계까지 성공적으로 도달하였으면, 최종 목표에 따라서 책을 보아야 하는 단계이므로 더 이상 일반적인 추천이 가능하지 않다. 그리고 여기까지가 튜토리얼이다

7. 관련 문서

8. 외부 링크


  1. [1] 예제 코드나 대회 제출용 코드를 보면, 헤더 파일 호출 부분 아래에 using namespace std;를 입력하여 std 네임스페이스를 생략하는 경우가 있는데, 실무에서는 사용하지 말아야 한다. 다른 네임스페이스에 같은 이름의 함수나 변수가 존재할 수 있으며, 이러한 경우 using문으로 전역 네임스페이스에 포함하면 충돌이 일어날 수 있다.
  2. [2] 읽는 법. 발음을 자주 물어 보는지 아예 본인이 wav 파일로 읽는 법을 올려두었다.
  3. [3] 간단하게 'C=C+1' 정도 의미다. 수학교육과 등에서는 '다음수'라는 개념으로 가르친다. 간단히 for문에 들어가는 for (i = 0; i < 10; i++) { foo.bar(); } 을 생각하면 쉽다.
  4. [4] 이를 구현한 대표적인 예로는 GCC의 libstdc++, LLVM/Clang의 libc++가 있다.
  5. [5] "잘 짜인 C 프로그램은 C++ 컴파일러로 컴파일할 수 있어야 한다"는 말도 1999년에 C99 표준이 나오면서 틀린 말이 되었다.
  6. [6] C로 프로그래밍에 발을 들어 놓은 후 C++/Java로 업그레이드(...) 테크를 타는 학생에게 C++ 프로그래밍을 해 오라 하면, C에서는 구조체라는 개념이 있더라도 잘해봐야 C++의 입출력 객체를 이용하는 정도를 넘지 못하고 절차적 프로그래밍을 그대로 따라가는 영락없는 C 스타일의 프로그램이 튀어나오기 일쑤다.
  7. [7] 원본 객체가 필요없는 경우, 새로 객체를 생성할 때 원본 객체의 포인터와 값들을 그대로 사용하여, 오버헤드 없이 기존 객체의 메모리를 재활용할 수 있도록 하는 기능이다.
  8. [8] Resource Acquisition Is Initialization. 이를 직역하면 "자원 흭득은/이 초기화다."는 뜻으로, 실행 흐름이 특정 범위를 벗어나면 그 안에서 할당한 자원을 다시 반납하도록 만드는 디자인 패턴이다. 해당 범위의 시작과 끝에 newdelete를 사용하여 동적 배열과 같은 힙 영역 메모리의 관리를 직접 수행할 수도 있지만, 이를 하나의 객체에 묶음으로써 해당 범위에서 자동적으로 생성자와 소멸자가 호출되어 메모리를 자동으로 관리되도록 할 수 있다. 이러한 디자인을 이용한 설계는 메모리 누수를 예방할 수 있다는 장점이 있다. 이에 대한 대표적인 예시는 스마트 포인터인 std::unique_ptr뮤텍스std::lock_guard가 대표적인 예이다.
  9. [9] 사실 이것도 대형 게임에나 해당되는 말이고, 간단한 모바일 게임은 유니티 엔진의 사용 확대로 인해 C#으로 많이 대체되었다.
  10. [10] 아이러니한 건, 유니티 엔진도 내부 소스는 C++로 이루어져 있다는 사실이다. 즉, 스크립팅용으로만 C#을 쓰는 셈이다. 심지어 C#으로 짜놓은 코드를 C++로 변환하는 IL2CPP라는 기능도 제공한다.
  11. [11] 비아네 스트로스트룹이 직접 쓴 TC++PL이 1,300쪽, C++ Primer가 1,700쪽이다. 그런데 TC++PL이나 Primer나 둘 다 초보자에게 권할 만한 책은 아니다. TC++PL의 서문에는 아예 'C++를 시작하는 입문자에겐 이 책을 권장하지 않는다'는 말이 적혀 있다.
  12. [12] 단, unsafe scope에서 발생한 메모리 버그나, 스마트 포인터의 순환 참조 등으로 인한 메모리 누수는 여전히 발생할 수 있다. 언어적 차원에서 이러한 실수를 덜하도록 고려되었지만, 여전히 다른 언어와 마찬가지로 항상 안전하지는 않다는 점에 유의하자.
  13. [13] 예외를 던지지 않는 실행경로에서는 아무 것도 하지 않아도 되도록 만드는 기법. 요약하면 컴파일러가 전역 에러 핸들러 테이블을 미리 만들어두어, 예외를 던지면 런타임에 해당 테이블을 참조하여 처리하는 방법이다.
  14. [14] 처리하기 전에 앞서 확인하는 과정을 통해 피할 수 있는 예외는 당연히 피하는 것이 좋다. 예를 들면, std::optional에 유효한 객체를 가지고 있지 않을 때 객체에 접근하여 발생하는 std::bad_optional_access 예외는 접근하기 전에 미리 확인하여 피할 수 있다. 그러나 컨테이너 라이브러리 등을 사용하는 과정에서 std::bad_alloc과 같이 메모리 할당의 실패로 발생하는 예외는 남은 메모리 크기를 확인하더라도 피하지 못할 수 있다. 이 경우, 예외를 처리하지 않으면 프로그램은 종료될 뿐이다.
  15. [15] two-phase construction 등 회피 꼼수가 있으나 어디까지나 꼼수다. C++ 특성상 예외를 완전히 피하면서 깔끔한 코드를 설계하는 것은 매우 어렵다.
  16. [16] 메이저 컴파일러들 중 일부는 RTTI와 마찬가지로 예외 처리를 끄는 기능을 제공하긴 한다. 하지만 이 경우 표준 라이브러리를 사용하는 중 거기서 예외를 던지는 순간 정의되지 않은 동작(undefined behavior)으로 돌입한다. 이 순간에는, 컴파일러나 런타임 라이브러리에 따라 다르긴 하지만, 대부분의 경우 프로그램이 종료된다.
  17. [17] P0709에 따르면 무려 52%의 프로그래머가 프로젝트 내에서 예외가 금지되었다고 답하였다.
  18. [18] 이러한 개념을 먼저 도입한 언어의 예로 RustKotlinResult를 들 수 있다. Rust는 예외 대신 Result 객체와 열거형 에러를 사용하며, 다른 열거형으로의 변환에 대한 구현이 있는 경우 자동 변환을 지원한다.
  19. [19] C++뿐 아니라 C, 파스칼 등 클래식 언어들이 전부 ISO에서 표준화되고 있다. 아예 이런 언어를 표준화하는 전문 태스크 팀 JTC1/SC22라는 게 있으며, 이중 C++는 21번째 그룹에 속한다.
  20. [20] 아예 boost 라이브러리 개발진 중 다수가 C++ 표준화 위원이기도 하다. 차세대 C++에 들어갈 요소를 표준화 위원들이 미리 개발해서 시험해본 것이라 봐도 무방하다.
  21. [21] 객체의 복사에서 원본이 불필요한 경우 이를 활용하면 실행 시간 감소의 효과가 있다. 복사 생성자를 통해 객체를 복사하고 원본의 메모리를 삭제하는 대신, 얕은 복사처럼 기존의 포인터만 가져오고 원본 역시 포인터만을 소멸시켜 객체 자체는 그대로 사용한다. 이동 생성자와 이동 대입 연산자를 통해 이러한 기능을 구현한다.
  22. [22] 표준 라이브러리의 iterator 대신 이를 대신 사용함으로써 for문을 간략하게 작성할 수 있다.
  23. [23] 기존 표준에서 유일한 스마트 포인터였던 std::auto_ptr은 명시적이지 않은 소유권 이동, 동적 배열의 삭제 불가 등 실무에서 절대 사용하면 안되는 쓰레기였지만, std::shared_ptr와 같은 새로운 스마트 포인터가 추가되면서 메모리 관리가 보다 안전해지고 편해졌다. 스마트 포인터를 잘 활용한다면 메모리 관리에 대한 수고를 덜 수 있다. 실제로 Chromium과 같은 대규모 프로젝트에서 심심치 않게 볼 수 있다.
  24. [24] 다만, 속도가 Python보다도 1.5배나 느리다. 컴파일러들의 구현이 좋지 않거나 운영체제의 메모리 할당이 너무 잦기 때문으로 보인다. 자주 사용한다면 별도의 라이브러리를 사용할 것을 추천한다.
  25. [25] C++11 제정 당시에도 std::auto_ptr 등 많은 옛날 요소들이 deprecated로 명시되어 사용이 자제될 것을 권장받았으나, 이는 어디까지나 권고라 사용자가 맘 놓고 쓰려면 쓸 수 있었다. 이전 버전의 기능을 아예 죽여버린 사례는, C++98 시절에 실상 누구도 안 썼고 지금은 아예 용도가 바뀌어버린 auto 등 극소수만이 해당한다.
  26. [26] C/C++의 특수기호는 미국의 부호체계인 ASCII를 기반으로 하고 있기 때문에 해당 영역에 다른 문자를 넣어 사용하던 유럽국가들의 코드페이지에서는 이상하게 표현되는 일이 있었다. 그래서 잘 사용하지 않는 문자의 조합으로 특수문자를 표기하는 방법. 문자 2개의 조합이면 digraph, 3개면 trigraph. 유니코드가 대세가 된 지금은 사실 필요 없고, 옛날 소스라고 해도 간단한 일괄 변환작업으로 새 표준에 맞출 수 있기도 하다. 다만 표준에서 삭제처리를 할 경우 해당 문법이 다른 기능으로 대체될 수 있기 때문에 일괄변환이 언제나 될 것이라는 보증이 안 되고, 후술하는 IBM 등 장기간의 낡은 코드와 관련된 이해관계자들 입장에서 떨떠름한 것은 사실이다.
  27. [27] 다만 이를 제대로 지원하는 컴파일러는 그다지 많지 않아서 호환성 문제가 있을 수 있다. Visual Studio의 경우 2017 이상, GCC의 libstdc++의 경우 9 이상에서 지원한다.
  28. [28] 언어 차원에서 지원하기 때문에, 비동기 프로그래밍을 위해 별도의 라이브러리가 필요하지 않게 된다.
  29. [29] 소스 파일이 각각의 헤더 파일을 일일이 포함하는 대신, 미리 컴파일된 모듈을 사용함으로써 컴파일 속도가 크게 향상된다. 별도의 시범 사양으로도 공개되어 있기 때문에, Visual Studio의 C++ 컴파일러와 LLVM/Clang 컴파일러에서 이미 시범적으로 지원하고 있다.
  30. [30] 현존하는 템플릿 트릭의 대다수를 대체할 수 있으므로 코드가 간결해지고 가독성이 증가한다.
  31. [31] 3년 주기를 따를 경우 C++23이 된다.
  32. [32] 줄여서 Managed C++라고도 불렀다.
  33. [33] 게임은 일반적으로 생산성이 매우 중요한 거대 프로젝트이면서 하드웨어의 성능을 극한까지 끌어내야 하는, 속도와 효율에 매우 민감한 특수한 소프트웨어 분야이기 때문에 C++가 많이 사용된다. 괜히 이런 이야기에 게임에 대한 언급이 잘 나오는 게 아니다.
  34. [34] 초기의 Managed C++은 언어의 포지션도 상당히 애매했다. 간단히 말하자면 닷넷 프레임워크와 레거시 운영체제 프레임워크를 동시에 지원하고자 했으나 결과는 이도저도 아니었다. 차라리 닷넷 프레임워크가 COM을 지원하니 그런 걸 쓰는 게 낫다.
  35. [35] 시스템, 실시간 임베디드, 게임, 파이낸스, 그래픽, 머신러닝, 수치 시뮬레이션...
  36. [36] 하지만 한국어판은 번역 오류가 많아 이해가 어려울 수도 있다
  37. [37] 대부분의 컴공과에서 배운다. 많은 컴공과에서 첫 언어로 C를 가르치는 관계로, 학생들에게 익숙한 C의 문법을 가지고 객체지향 프로그래밍의 개념도 다룰 수 있다는 장점이 있기 때문이다. Java나 Python을 처음으로 배우는 학교의 경우에도 언젠가 한번쯤은 C나 C++를 건드릴 일이 생기게 된다.

최종 확인 버전:

cc by-nc-sa 2.0 kr

Contents from Namu Wiki

Contact - 미러 (Namu)는 나무 위키의 표가 깨지는게 안타까워 만들어진 사이트입니다. (227.95ms)