C++
(♥ 0)
[각주]
import std; // 또는 import <print>;
int main() {
std::println("Hello, world!");
}
1. 개요[편집]
덴마크의 컴퓨터 과학자 비야네 스트로스트룹(Bjarne Stroustrup)[1] 가 C언어를 바탕으로 만들었다. 1979년에 C언어에서 직접적으로 파생된 C with Classes라는 이름의 언어로 시작되었다가, 1983년에 지금의 이름을 갖게 되었다. 그리고 객체 지향 및 일반화 프로그래밍과 같은 멀티 패러다임을 지원하는 프로그래밍 언어이다.“A light-weight abstraction programming language”
“가볍게 추상화한 프로그래밍 언어”
- Bjarne Stroustrup
C언어에서
++
라는 것은 원래 값에 1을 더해서 대입하라는 뜻이다.[2] C는 B 언어를 계승한다는 의미에서 C가 되었는데 왜 D가 아니라 C++가 되었냐 하면, C 언어를 거의 그대로 두고 필요한 만큼만 향상시켰기 때문이라고 한다. 비슷한 이름인 C\#과의 차이도 이러한 점에서 나타난다.쓸 때는 C++라고 쓰지만 읽을 때는 시 플러스 플러스, 혹은 줄여서 시플플이라고 읽는다. 한국에서는 사실상 속어에 가까운 씨쁠쁠로 불린다. '씨뿔뿔'로 발음되는 경우도 많다. 나이가 꽤 있는 교수 중에는 간혹 '시 더블 플러스'라고 읽는 경우도 있다. 미국에서는 그냥 씨 플러스 플러스 또는 확장자 명인 CPP(시피피)라고 읽는다.한국에서 컴퓨터공학과를 다닐 경우 배울 수 있는 프로그래밍 언어 3개 중에 속한다. 나머지 둘은 C와 Java.[3] 학교마다 다르지만 1학년때 아예 다른 C, Python을 배우고 2학년 1학기때 Java, 2학기때 C++ 과목을 수강하는 곳도 있다. 예시로 서울대학교의 경우에는 1학년 1학기에 Python으로 코딩을 입문하고, 1학년 2학기에 C언어로 어느 정도 큰 프로젝트를 짜 보고, 2학년 1학기에 C++과 Java를 배우면서 객체지향 프로그래밍을 배우게 된다.
온라인 상으로 코드를 실행시켜 보고 싶다면 여기로.
C++의 창시자는 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.[4]
1.1. C와의 차이점[편집]
초기 C++가 C언어를 기반으로 시작했기 때문에 지금도 대부분의 C 프로그램은 C++ 컴파일러에서도 문제없이 컴파일된다. 초기 C++ 컴파일러는 일단 C++ 코드를 C로 변환하고 그걸 C로 재컴파일하는 방식을 사용했을 정도. 다만, C가 항상 C++의 부분집합이었던 것은 아니라 지금도 구조체같이 몇몇 부분에서 차이점이 있다. "잘 짜인 C 프로그램은 C++ 컴파일러로 컴파일할 수 있어야 한다"는 말도 1999년에 C99 표준이 나오면서 틀린 말이 되었다. 순수 C 소스 코드를 C++로 컴파일 할 때 문제의 여지가 생길 가능성이 있다.
그러나 C++는 C와 프로그래밍 패러다임이 크게 다르다. C는 태초부터 절차 지향으로 설계된 운영체제를 만들기 위한 언어였으며, 지금도 메모리와 각종 저수준(low level) 프로그래밍을 위한 언어이기 때문이다[5] . 반면 C++는 절차 지향, 객체 지향, 일반화 프로그래밍, 함수형 프로그래밍을 모두 지원하는 언어다. 그래서 C로 작성된 프로그램에서 C++ 방식으로 코딩하려면 해당 코드에서 C++에 새로 도입된 것을 추가하는 게 아니라, 설계부터 시작해서 완전히 새로 해야 하는 경우가 많다. 곧 C를 알고 있다고 C++를 쉽게 할 수 있는 것은 아니다. 세간에서 C, C++ 중 어디가 쉽다라던가 하는 건 어디까지나 간단한 프로그램의 예일 뿐이다. 또한 C++의 객체지향이 다른 객체지향 언어에 비해 이해하기가 만만한 개념이 아닌 데다가 C++의 객체지향은 C언어를 기저에 유지하면서 여러 구성 요소를 추가했기 때문에 신경 써야 할 부분이 많다. 그래서 다른 객체지향 언어에서보다 잘 다루는데 더 많은 공부가 필요하다. C++을 잘 활용하려면 자료구조 및 알고리즘, 입출력 스트림, 그리고 적어도 다른 언어에서 일반화 프로그래밍이 쓰이는 것처럼 템플릿을 활용할 수는 있어야 한다. 그래서 C언어를 알고 있는 사람이 C++ 초보자용 교재를 1권 끝내고 프로그램을 만들어 보라고 해도, 대부분 C++의 입출력 객체를 이용하는 정도를 넘지 못하고 절차적 프로그래밍을 그대로 따라가는 영락없는 C 방식에서 벗어나지 못한다.
C++에서 C 전향도 만만치가 않다. 절차지향 언어의 사고방식이 머릿속에 굳어버려 코드를 전환하는 데 방해가 된다. C++가 C의 모든 기능을 포괄하고 있으므로 C++를 할 줄 알면 C도 할 줄 안다고 생각하기 쉽지만, 사실 C++가 명시적으로 비교적 간단히 사용할 수 있도록 제공하는 기능들을 C에서는 암묵적으로 여러 가지 수많은 '트릭'을 통해서 쥐어짜내듯이 만들어 사용하는 경우가 많다. 지금도 많은 대학에서 컴공 1학년 1학기 때 C를 먼저 가르치고 빠르면 2학기, 늦어도 2학년에 C++를 가르치지만, 반면에 교수가 절차 지향이 머리에 굳어버린다며 C++와 객체를 먼저 가르치고 C는 아예 건드리지도 않고 다른 하드웨어 관련 학과에서만 가르치는 대학들도 많다.
따라서, 초심자 입장에서는 C와 C++는 완전히 다른 언어로 파악하고 접근해야 한다.
struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order)
int arr[3] = {[1] = 5}; // valid C, invalid C++ (array)
struct B b = {.a.x = 0}; // valid C, invalid C++ (nested)
struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed)
[6]_Generic(type)
을 사용한 자료형에 대해 안전한 매크로의 지원 등 C에서만 지원되는 문법이 몇몇 있다.클래스 비교 코드 // C 스타일 typedef struct GameObjectTag { long id; float x, y, z; } GameObject; static GameObject* CreateObject() { static long __gobject_id = 1; GameObject* result = (GameObject*) malloc(sizeof GameObject); if (result) { result->id = __gobject_id++; } return result; } static void InitObject(GameObject* obj, long id) { obj->id = id; obj->x = 0; obj->y = 0; obj->z = 0; } static void InitObject(GameObject* obj, long id, float x, float y, float z) { obj->id = id; obj->x = x; obj->y = y; obj->z = z; } static void MoveObject(GameObject* obj, float x, float y, float z) { obj->x = x; obj->y = y; obj->z = z; } #include <iostream> struct Foo { int n ; Foo ( ) { std :: cout << "statikus konstruktor \n " ; } ~Foo ( ) { std :: cout << "statikus destruktor \n " ; } } ; Foo f ; // statikus objektum int main ( ) { std :: cout << "főfüggvény \n " ; } // C++ 스타일 class GameObject { public: constexpr GameObject(const long& new_id, const float& cx = 0, const float& cy = 0, const float& cz = 0) noexcept : id(new_id), x(cx), y(cy), z(cz) {} constexpr void Move(const float& x= 0, const float& y = 0, const float& z = 0) noexcept { this->x = x; this->y = y; this->z = z; } [[nodiscard]] constexpr std::tuple<float&, float&, float&> GetPosition() noexcept { return std::tie(x, y, z); } [[nodiscard]] constexpr std::tuple<const float&, const float&, const float&> GetPosition() const noexcept { return std::tie(x, y, z); } long myID = -1; // 기본값 float x, y, z; }
this
를 활용하면 인자 전달 없이 클래스에 속한 메서드 호출만 할 수 있다. 그외에 기본 연산자 오버로딩, friend
를 통해 외부 클래스 또는 함수와 속성 공유를 할 수 있다. std::ios_base::sync_with_stdio(false);
iostream
이나 fstream
등 입출력 스트림이 C의 입출력 함수보다 상당히 느리다. C++의 입출력 스트림은 예외없는 입출력을 위해 try
문은 물론 온갖 내부 비트 플래그가 돌아가고 있기 때문이다. 만약 프로그램내에서 std::cin
, std::cout
등 C++ 스트림[iostream] 계열만 사용하고 stdin
, stdout
등 표준 C 스트림[stdio.h] 을 사용하지 않는다면, 각 입출력 스트림간의 동기화를 정지시켜 iostream에 비약적인 속도 향상을 가져올 수 있다 [7] .1.2. 객체 지향 프로그래밍[편집]
C++는 객체 지향 프로그래밍을 지원하지만, C++의 객체지향은 다른 객체지향 언어에서와는 성격이 좀 다르다. 대다수의 객체지향 언어에서는 많은 부분을 런타임에 처리하며 메모리를 자동으로 관리해준다. C++은 기반 프레임워크가 존재하지 않아 클래스가 어떤 일급 객체가 아니라, 일반 변수처럼 메모리 덩어리일 뿐이다. 최대한 많은 것을 컴파일 타임에 처리하는 것을 지향하고 동적인 메모리 할당을 지양한다. 그리고 결정적으로 메모리 등을 프로그래머가 직접 관리하게 하기 때문에 클래스를 포함해 전반적인 프로그램 설계 자체가 상당한 차이를 보이게 된다.
Java 등의 다른 객체지향 언어에서와 같은 방식으로 C++ 클래스를 디자인하면 거의 틀림없이 런타임 오류 또는 메모리 문제가 발생한다. 특히 쓰레기 수집을 지원하는 Java로 입문하여 C++로 갈아타는 테크를 탄 학생이라면 처음에는 메모리가 줄줄 새는 프로그램을 만들게 될 것이다. 반면 C++ 스타일을 숙지하고 다른 객체지향 언어에서 프로그래밍을 하는 경우 특별히 안 될 것은 없지만, 해당 언어의 원어민 스타일로 작성한 코드에 비해 시간이 많이 걸리고 클래스 및 프로그램 구조가 지나치게 경직되는 경향이 있다. 소멸자가 호출되지 않는 등의 차이점은 있지만 심각한 문제가 되는 경우는 많지 않다.
이러한 차이가 생기는 것은 대부분의 객체지향 언어는 직접적으로 Smalltalk의 영향을 받은 반면, C++는 Smalltalk보다 먼저 객체지향의 초보적인 개념을 제시한 시뮬레이션 전용 언어인 Simula에서 직접 영향을 받았기 때문이다. [8] 이는 C 프로그램과의 호환성을 고려한 결과이기도 하지만, 기본적으로 C++에서 프로그램의 성능을 희생시키지 않기 위해서였다. Smalltalk의 경우 당시의 기술적 한계도 있고 해서 C보다 대체로 수십 배 정도 느렸고, 이는 당시 C++에서 지향하는 결과가 아니었기 때문이다. 이런 차이 때문에 Java, C# 등의 언어를 먼저 접한 프로그래머들은 C++의 객체지향이 짝퉁이라며 싫어하기도 한다. 하지만 순수 객체지향 프로그래밍 언어임을 전면에 내세우는 Java나 C#과 달리, 객체지향 패러다임도 지원하는 다중 패러다임 언어인 C++ 입장에서는 얼마나 순수하게 객체지향의 이상을 잘 따르는가보다, C++가 제공하는 온갖 패러다임까지 아우르는 내적 일관성이 더 중요하므로 해당 언어들과 C++를 동일선상에 놓고 비교할 수는 없다.
대표적인 예시로 인자로 전달하는 값은 기본적으로 복사되는 것, 함수 내부에서 객체를 동적 할당하고 주소를 반환하는 것이 있다. 이는 C++에선 메모리 문제를 일으키기 쉬운 방식이라서 설계 단계부터 이런 동작을 배제해야할 필요가 있다. 거의 대부분의 프로그램은 실행되고 있는 현재 문맥에서의 지역 변수를 스택 영역에 저장하는데, 한 문맥의 실행이 완료되면 그 함수의 스택 영역 변수를 위한 메모리는 모두 삭제된다. 그리고 이 변수는 쓰레기 데이터로 남아있다가 다른 함수를 호출할 때 덮어씌워진다. 공유 자원과 같은 특정 데이터를 유지하려면 힙 영역에 할당해야 한다. 가비지 컬렉션이 자동으로 되는 언어에서는 언어 설계 차원에서 메모리에 대한 걱정을 덜기 위해 사용되지 않는 메모리를 자동으로 해제 해주기 때문에 매우 흔히 사용되는 방법이다. [9]
그러나 문제가 생기는 건 오래된 스타일의 C++ 코드에서나 그런 것이고, Modern C++에서는 스마트 포인터를 사용하거나 이를 이동 연산[10] 을 통해 그들과 완전히 같은 구조를 구현 가능하기 때문에 더 이상 해당이 없는 사항이다. 예를 들어 한 동적 객체를 여러 소유권자가 공유하는 경우 (참조 대상에 대한 횟수를 기록하는) 레퍼런스 카운팅을 지원하는
std::shared_ptr
를, 단일 소유자만 존재하는 경우 std::unique_ptr
를 사용한다 [11] .1.3. 일반화 프로그래밍[편집]
C++에서는 템플릿을 이용한 일반화 프로그래밍(Generic Programming)이 매우 폭넓게 사용된다. 특히 C++11을 시작으로 하는 모던 C++는 일반화 프로그래밍을 빼고 이야기하는 것이 불가능하다. 당장 매우 널리 사용되는 문자열 클래스
std::string
만해도 실제로 들여다보면 std::basic_string<char, std::char_traits<char>>
와 같은 형태의 클래스 템플릿의 특수화에 지나지 않는다.일반화 프로그래밍의 결과물으로는 C++ 표준 라이브러리의 일부분으로 포함된 컨테이너, STL 같은 것들이 있으므로, C++ 표준 라이브러리를 사용하는 순간 일반화 프로그래밍의 도움을 받는 것이다. 따라서 '나는 C++를 사용하지만 템플릿을 이용한 일반화 프로그래밍은 어려우니까 패스하겠다'는 말은 애초에 성립하지 않는다. 또 다른 예시로, C와는 달리 배열 생성에 포인터 혹은 []을 사용하는 배열 대신에
std::vector
를 기본으로 사용하라고 가르치는데, 이 std::vector
또한 저장될 원소의 자료형을 std::vector<int>
와 같이 템플릿 매개변수로 받아들이는 템플릿 클래스다. 중급 이하 개발자는 라이브러리를 그냥 가져다 쓰면 되고, 직접 만들어 쓸 필요는 없다. 이는 중급 개발자 정도로는 템플릿을 사용한 일반화 프로그래밍 기법을 정확하게 적용하는 것이 무척 까다롭고, 디버깅할 때 이해 불가능한 컴파일러 메세지를 받게 되는 경우가 많아 오류를 수정하기도 어렵기 때문이다. 현재는 C++20에서 concept
의 등장으로 이전보다 상대적으로 알아보기 쉬운 템플릿 작성이 가능해졌고, 컴파일 오류 메세지또한 좀 더 이해하기 쉽고 간결하게 표시되게 되었다.이런 이유로 C++ 학습 초반에 템플릿 프로그래밍을 직접 하는 것을 피하고 기껏해야 컨테이너 클래스만 사용하는 습관이 들다 보니, C++는 단순히 객체 지향 언어이고 STL이라는 템플릿 라이브러리를 덤으로 쓸 수 있는 정도라는 오해가 널리 퍼진 것이라 생각된다. 하지만, 특수 목적의 컨테이너를 설계하거나 범용 라이브러리를 설계하는 수준의 고급 개발자가 되려면 템플릿을 사용한 일반화 프로그래밍을 해야 한다.
Java나 C\#에서 찾아볼 수 있는 Generics가 지금 설명한 C++ 일반화 프로그래밍의 아주 제한된 형태의 적용례에 해당한다.
1.4. 메타 프로그래밍[편집]
C++에서는 템플릿을 사용해 메타 프로그래밍을 할 수 있다. 여기서 메타란 Meta Data (서술 자료)를 의미한다. 값 뿐만 아니라 프로그램, 바이너리, 속성 그 자체도 데이터로 취급한다. 가령 Python의 메타 클래스는 '클래스'를 생성하며 [12] 클래스의 바이트 크기, 필드, 메서드 등을 정의한다. 또한 Python, C\#, Swift 등 많은 언어가TMP (Template Meta Programming). 템플릿을 사용한 메타 프로그래밍 Wikipedia
int
, double
같은 원시 자료형도 클래스로 보여준다. 하지만 C++은 그럴 수가 없다. C++의 클래스는 메모리 덩어리고, int
라는 단어 자체에는 그저 컴파일러가 int
를 메타 정보로 가진 객체는 32비트 크기로 읽겠다, 말고는 아무 의미도 없다. 다른 언어는 '정보[13] 의 메타 정보[14] 의 메타 정보[15] 를 언어 자체적으로 가져오고, 심지어 정의할 수도 있다. C\#, JavaScript, Python이 대표적인 예시다. 반면 C++에선 클래스가 그냥 메모리 덩어리고 메서드도 그냥 쓰기 좋게 만든 함수 포인터다. 전처리기, 컴파일러 확장, 미리 쓰여진 코드에 의존할 뿐 자료형 자체에 대해서는 아무것도 알 수 없다. sizeof
, alignof
, alignas
키워드가 있지만 부족하다. 그리고 직접 클래스 자체의 속성을 정의하려면 using
, static
변수를 사용해 일일히 모든 클래스에 메타 정보를 기입해야 한다. 이 방법은 임의의 클래스, 그리고 원시 자료형에는 적용할 수 없는 문제가 있다. 이 간극을 메우기 위해 대신 템플릿을 사용해 메타 프로그래밍을 시도하는 것이다. 같은 템플릿 문법을 사용하지만 앞 문단의 일반화 프로그래밍과는 엄연히 목표가 다르다. 일반화 프로그래밍이 자료형을 드러내어 컴파일 시기에든 실행 시기에든 코드 확장 및 범용성 증대를 목표로 한다면, 메타 프로그래밍은 명시해야할 자료형을 숨기고, 어떤 자료형에 대해 그 자료형에 대한 정보를 기술하는 것을 목표로 한다. 이는 일반화 프로그래밍과는 다르게 확장이 아니고 특정 자료형에만 코드를 생성하도록 만든다.
- SFINAE(Substitution Failure Is Not An Error): 함수를 오버로딩 하는데 있어 조건에 따라 일부러 오류가 발생하는 템플릿의 구현 코드를 발생시켜 틀리지 않은 특정 구현만 선택되게 만드는 테크닉이다. C++의 템플릿은 자료형의 상태를 서술하기 위해 분명히 존재하지만, 실제로 인스턴스화되는 시점은 런타임 직전 사용자의 코드에서 실제 자료형을 가져와야 한다. C++ 문법의 틈새에 존재하는 문법 오류이지만 런타임 오류가 아닌 상황을 적극 활용하는 것이다. 직역하면 '대입 실패는 오류가 아니다'라는 뜻이다.
그렇다면 C++에서 그냥 메타 프로그래밍이라고 안하고 굳이 앞에 템플릿이라고 붙인 이유가 있을까? C++의 템플릿은 독특한 특성을 많이 가지고 있다. 템플릿 매개변수는 반드시 컴파일 시점에 결정된다. 템플릿 매개변수는 자료형 뿐만 아니라 값도 전달할 수 있다. 템플릿의 실제 코드는 늦게 평가된다. 템플릿은 분명 존재하는 C++ 코드이지만, 실질적으로 전처리기 구문과 다름이 없다. 실행 시점에는 이미 바이너리로 모든 경우에 대해 완성된 상태가 되므로 컴파일 이후에 실행 시간에는 아무 영향이 없다 [16] . 컴파일러가 예측할 수 없는[17] 네트워크 접근, 다중 스레드 작업, 그리고 운영제체 호출을 제외하면 모두 상수 시간에 결정할 수 있다. 곧 C++의 메타 프로그래밍은 자료형의 상태를 기술하는 것에 더해서, 할 수 있는 작업은 컴파일러를 고문해 모조리 미리 처리하는 것이 지상과제가 된다. 이는 C++의 템플릿 문법이 컴파일 시간에 Turing complete하기 때문에 이런 일이 가능한 것이다. C++ 안에 컴파일러 전용의 또 다른 언어가 숨어있는 것과 같은 상황이다. 다른 언어에서는 비슷한 것도 찾기 힘들다.
* Expression Templates: 디자인 패턴의 일종인 Proxy pattern 기반의 Lazy evaluation이 적용되는 효율적인 계산 코드를 컴파일 시점에 생성하는 기법이다. 일반적으로 연산 도중의 임시 객체 생성 문제를 이 기법을 통해 해결하는 경우가 있다. RVO(Return Value Optimization)를 감안하더라도 C++ 특성상 연산자를 활용하는 과정에서, 직접적인 연산을 시도하면 임시 객체의 생성을 완전히 막을 수는 없기 때문이다.
배우기 어렵고 알아보기도 힘들고, 실행 성능을 포기하지 않는 대신 컴파일 시간을 심각하게 포기했다. 심지어 디버깅도 힘들다. 다만 디버그 문제는 자기가 원하는 템플릿에 맞춰 중단점을 거는 게 가능해지는 등 많이 개선된 편이다. 당연히 템플릿에 대하여 깊은 이해가 없다면 아예 이해할 수가 없는 개념이기도 하다. 그래도 알아두면 은근 써먹을 데가 많다. C++에서는 단순히 특정 자료형의 속성 선언부터 함자 클래스[18] 를 이용한 방문자 패턴, 트레잇, 타입 리스트, CRTP, mixin 등 수많은 고성능 디자인 패턴은 C++에서는 TMP의 도움 없이는 시도조차 할 수 없다. C++17 기준으로 쓰여진 C++ 템플릿 기본서 는 그 쪽수가 800쪽을 넘는다.
예를 들어, 피보나치 수열을 계산하는 코드는 다음과 같이 쓸 수 있다.
constexpr size_t fibonacci(size_t n) noexcept
{
return (n < 2) ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
constexpr size_t result = fibonacci(7); // 13
아래는 템플릿을 활용해 컴파일 시점에 계산하는 코드이다. [19]
템플릿 코드 template <size_t N> struct Fibonacci { static constexpr size_t Value = Fibonacci<N - 1>::Value + Fibonacci<N - 2>::Value; }; template<> struct Fibonacci<0> { static constexpr size_t Value = 0; }; template<> struct Fibonacci<1> { static constexpr size_t Value = 1; }; int arr[Fibonacci<7>::Value];
C++17의if constepxr
또는 C++20의consteval
을 활용하면 더 간결하게 쓸 수 있다.template<size_t N> constexpr size_t Fibonacci() noexcept // C++20 이후에는 consteval로 지정해도 문제없다. { if constexpr (N < 2) return N; else return Fibonacci<N - 1>() + Fibonacci<N - 2>(); } int arr[Fibonacci<7>()];
실무에서 가장 많이 쓰이는 테크닉 중 하나로 CRTP (Curiously Recursive Template Pattern) 패턴이 있다. 추상 클래스를 구현할 시 가상 함수 호출에 따른 오버헤드를 막고자 고안되었다. 이는 다음과 같이 쓴다.
CRTP 예제 코드 template <typename Derived> class Base { constexpr void Function() noexcept(noexcept(Cast()->Function())) { Cast()->Function(); } protected: constexpr Derived* Cast() noexcept { return static_cast<Derived*>(this); } constexpr const Derived* Cast() const noexcept { return static_cast<const Derived*>(this); } }; class CRTPDerived1 : public Base<CRTPDerived1> { public: void Function() noexcept // Base::Function은 상수식이 아니고 noexcept(true) { std::print("CRTPDerived1"); } }; template <typename... Ts> class CRTPDerived2 : public Base<CRTPDerived2<Ts...>> { public: constexpr void Function() // Base::Function은 상수식이지만 noexcept(false) { throw "CRTPDerived2"; } };
1.5. 용도[편집]
본래 C는 유닉스라는 OS를 만들기 위해 어셈블리 대체용으로 만들어진 언어라, 머신 컨트롤의 저수준 작업이 주된 임무 중 하나였다. C++은 그 이름에서 보이듯이 사실상 C를 대체하기 위한 언어였기 때문에 여러 가지 고수준의 추상 기능들을 집어넣어 추상적인 시스템 위에서만 노는 게 아니라, 저수준의 기계 제어까지 가능한 추상화라는 요상한 철학을 지지한다. 뭔가 딜레마 같지만 C++과 비슷한 정도의 고수준 기능을 제공하는 언어는 절대로 C++만큼 복잡하지 않다. 여타 언어들이 객체 지향 프로그래밍을 구현하면서 동적 바인딩(C++의 가상 함수)을 디폴트로 쓰고 쓰레기 수집을 지원할 때 C++은 정적 바인딩을 디폴트로 하고 수동 메모리 관리를 유지한 것이다. 일반적으로 추상화 수준이 높은 언어일수록 프로그래머 머리로 해야할 구체적인 것들을 컴퓨터가 대신 해주고, 이것이 항상 최적화된 방식은 아니기 때문에 프로그램의 실행 속도가 상대적으로 느리게 마련이지만, C++ 프로그램은 위와 같은 이유로 성능 하락이 거의 없다.
C++과 비슷한 정도의 기능을 가진 언어 중에서 C++만큼 빠른 실행 성능을 내는 언어 구현은 흔치 않다. 대신 프로그래머가 언어의 이상한 부분까지 신경쓰지 않으면 안 되는 디자인이 되어 제대로 쓰는 게 굉장히 어려운 언어가 되고 말았다. 추상화의 가장 큰 이유와 장점은 그것을 추상화시킴으로서 그 아래 감춰진 디테일한 부분을 신경쓸 필요가 없게 만드는 것이다. 따라서 저수준 제어와 고수준 추상화의 두 가지 개념은 서로 완전히 충돌하는 부분이다. C++는 저수준 제어를 포기하지 않았기 때문에 프로그래머는 C++로 저수준 작업을 할 때는 디테일한 부분까지 신경써야만 한다. 반대로 C++에서 고수준 기능을 사용하려면 프로그래머가 저런 부분을 감안하여 C++ 방식으로 변형되어 적용된 코딩 문화를 알고 있어야 한다.
애초에 C 언어의 기능을 모두 포함했던 이유는 생산성과 함께 C를 대체하겠다는 두마리의 토끼가 목적이었기 때문이다. 그러나, OS를 만드는 유닉스 커뮤니티들이 C++로의 전환을 거부해서 한마리 토끼는 놓쳤지만 그럼에도 Java라는 강력한 언어가 등장하기 전까지는 C에 비해 압도적인 생산성으로 어플리케이션 소프트웨어 쪽에서 순식간에 대세언어가 되었으나 Java의 등장과 각종 스크립트 언어들의 부상, 그리고 비주얼 베이직에서 Python으로 이어지는 하이퍼 고 생산성 언어들의 등쌀에 현재는 포지션이 좀 애매해진 경향이 있다. 애초에 C의 기능을 전부 포함했던게 공짜로 된 것이 아니라 그만큼 복잡해지고, 컴파일 속도까지 희생하는 역효과도 포함하고 있었고, 덕분에 Java나 C# 등의 기타 고생산성 언어들에 비해 생산성은 떨어지면서 정작 노리고 있던 C의 대체도 날아간 상황.
현재는 클라이언트 쪽에서는 성능이 엄청나게 중요하면서 동시에 개발속도도 크리티컬한 게임[20] , 포토샵, 웹 브라우저 등의 데스크탑 어플리케이션, 업무 연속성이 중요한 금융권 IT망 및 금융공학에서 주로 사용된다. 사실 그 외에도 퍼포먼스를 중시하는 경우 여전히 많이 사용된다. 구글에서도 상당히 많이 사용하는 언어이다.# 이미 구글에서 2009년에 개발한 컴파일 기반 언어인 Go가 나와 있었는데도 텐서플로가 C++로 개발되었다는 점이 하나의 예시이다.
2010년대에 이르러서는 CUDA의 강력한 존재 덕분에 인공지능 관련 수요가 급격하게 늘어났다.
2. 문법[편집]
"display: none; display: 문단=inline"를 참고하십시오.
3. 언어 명세[편집]
"display: none; display: 문단=inline"를 참고하십시오.
4. 표준 라이브러리[편집]
"display: none; display: 문단=inline"를 참고하십시오.
5. 역사[편집]
5.1. 모던 C++[편집]
각종 주요 컴파일러들과 모던 C++의 호환성은 여기서 볼 수 있다.