C++은 C를 기반으로 만든 객체지향언어(Object Oriented Language)이다. C++의 ++는 C/C++ 에서의 증감연산자로 +1을 시키는 연산자다. 즉, C에서 한 단계 올라간 언어라는 의미를 담고 있다. 여담으로 Microsoft에서 C++에 ++를 한 번 더 하여 만든 것이 C#이다. ++위나 아래에 ++를 붙이면 #이 되어 C#이 되었다.
C++는 C에서 파생된만큼 기존 C언어 라이브러리들과도 호환이 잘되며, C++만의 독자적인 라이브러리를 가지고 있어 C로 개발된 외부 라이브러리를 C++문법에 맞게 사용할 수도 있다. C와 Python 사이에 있는 언어로서 속도는 C에 가까우면서도 C보다는 코딩이 편하다. 다음은 이 문서에서 다룰 C++에 대한 내용이다.
기본 자료형
다음은 추가 라이브러리 필요없이 바로 쓸 수 있는 자료형이다. 각 자료형의 크기는 64bit ubuntu 20.04 LTS 버전의 g++ 9.3.0버전 기준으로 작성되었다. 자료형마다 할당되는 크기는 OS, CPU, 컴파일러 등에 따라 차이가 있을 수 있다.
| 유형 구분 | 자료형 | 크기 | 범위 | 비고 |
|---|---|---|---|---|
| 유형 없음 | void | - | - | - |
| 정수형 | bool | 1 byte | 1 or 0 | true or false |
| char(int8_t) | 1 byte | $-2^7 \sim 2^7-1$ ($-128 \sim 127$) | - | |
| unsigned char(uint8_t) | 1 byte | $0 \sim 2^8-1$ ($0 \sim 255$) | - | |
| short(int16_t) | 2 byte | $-2^{15} \sim 2^{15}-1$ ($-32,768 \sim 32,767$) | - | |
| unsigned short(uint16_t) | 2 byte | $0 \sim 2^{16}-1$ ($0 \sim 65,535$) | - | |
| int(int32_t) | 4 byte | $-2^{31} \sim 2^{31}-1$ ($-2,147,483,648 \sim 2,147,483,647$) | - | |
| unsigned int(uint32_t) | 4 byte | $0 \sim 2^{32}-1$ ($0 \sim 4,294,967,295$) | - | |
| long(int64_t) | 8 byte | $-2^{63} \sim 2^{63} -1$ ($-9,223,372,036,854,775,808 \sim 9,223,372,036,854,775,807$) | - | |
| unsigned long(uint64_t) | 8 byte | $ 0 \sim 2^{64}-1$ ($0 \sim 18,446,744,073,709,551,615$) | - | |
| 실수형 | float | 4 byte | $-3.402\ 823\ 4 \times 10^{-38} \sim 3.402\ 823\ 4 \times 10^{38}$ | 최대 정확도: 소수점 아래 7자리 |
| double | 8 byte | $-1.797\ 693\ 134\ 862\ 315\ 7 \times 10^{308} \sim 1.797\ 693\ 134\ 862\ 315\ 7 \times 10^{308}$ | 최대 정확도: 소수점 아래 15자리 | |
| long double | 16 bytes | $\pm1.189\ 731\ 495\ 357\ 231\ 765\ 085\ 759\ 326\ 628\ 007\ 016\ 2 \times 10^{4932}$ | 최대 정확도: 소수점 아래 37자리 |
long의 경우 long int, long long, long long int 로도 쓸 수 있다.
이 중 주로 사용하는 자료형은 char(문자, 문자열), int(정수), float(실수), double(실수) 이다.
자료형이 가진 범위를 넘어선 숫자를 계산할 경우 stack overflow가 일어나서 원치 않은 결과를 얻을 수 있다. 또 자신이 다룰 숫자에 비해 너무 큰 크기를 가진 자료형을 사용하게 되면 메모리 낭비가 될 수 있다. 따라서 자신이 다룰 숫자의 크기가 어느정도 될 지 가늠해 보고 적절한 자료형을 사용하는게 중요하다.
다시 한 번 강조하지만 위의 자료형 크기는 참조용이며, 자신의 PC 환경에 따라 조금씩은 다를 수 있다. 특히 long과 long double의 경우 PC에 따라 각각 4 byte, 8 byte를 할당하는 경우도 있으니 다음과 같이 확인해 보고 사용하는 것을 권장한다.
#include <iostream> using std::cout; int main() { cout << "long: " << sizeof(long) << "\n"; cout << "long double: " << sizeof(long double) << "\n"; return 0; }
라이브러리 추가가 필요한 자료형
이 문서에서는 추가 라이브러리의 include가 필요한 자료형을 다룬다.
| 자료 유형 | 속해 있는 라이브러리 | 역할 | 비고 |
|---|---|---|---|
| string | <string> | 문자열을 더 쉽게 다룰 수 있다. | - |
| vector | <vector> | 기존의 배열을 더 쉽게 다룰 수 있다. | - |
| map | <map> | key:value 쌍을 이용할 수 있다. | - |
| unordered_map | <unordered_map> | map과 같지만 key 기준 정렬이 일어나지 않아 정렬이 필요하지 않은 경우 성능상 map보다 유리하다. | - |
| pair | <utility> | 2개의 자료형을 하나의 객체로 묶을 수 있다. | <vector> 라이브러리에서 <utility>를 include 하고 있으므로 <vector>를 include 했다면 따로 할 필요가 없다. |
| tuple | <tuple> | 3개 이상의 자료형을 하나의 객체로 묶을 수 있다. | - |
| deque | <deque> | vector와 비슷하지만 pop_front(), pop_back() 같은 함수를 지원하여 가장 첫 원소 혹은 가장 마지막 원소만 제거할 수 있다. | - |
string
<string> 라이브러리에 있는 자료형으로 문자열을 좀 더 쉽게 다룰 수 있다. 한 예로 + 연산자로 서로 다른 문자열을 합칠 수 있다. 포함되어 있는 함수는 매우 많으나 주로 사용하는 몇 가지만 설명한다.
| 함수명 | 함수 설명 | 예시 |
|---|---|---|
| to_string(x) | int나 double 같은 숫자형 자료를 string 자료형으로 변환하여 반환한다. | to_string(3) $\rightarrow$ “3” |
| to_string(3.8) $\rightarrow$ “3.8” | ||
| stoi(x) | string 자료형을 int형 정수로 변환하여 반환한다. | stoi(“3”) $\rightarrow 3 |
| stoi(“3.8”) $\rightarrow$ 3 | ||
| stod(x) | string 자료형을 double형 실수로 변환하여 반환한다. | stod(“3”) $\rightarrow$ 3.0 |
| stod(“3.8”) $\rightarrow$ 3.8 | ||
| string.substr(start, count) | 문자열을 start 위치부터 start+count-1 위치까지 잘라서 string 자료형으로 반환한다. | “abcde”.substr(1,3) $\rightarrow$ “bcd” |
| string.c_str() | string 자료형인 문자열을 const char형 포인터로 변환하여 반환한다. | “abcde”.c_str() $\rightarrow$ “abcde”(type is const char*) |
substr의 경우 인자가 하나만 있으면 start위치부터 끝까지를 반환한다.
다음은 각 함수를 사용하는 코드다.
- | example_string.cpp
#include <iostream> #include <string> #include <typeinfo> using std::cout; using std::string; using std::to_string; using std::stoi; using std::stod; int main() { cout.precision(17); string str1 = "1234.56789"; int x = stoi(str1);// x = 1234 double xd = stod(str1); // xd = 1234.56789 string i_to_s = to_string(x); // i_to_s = "1234" string d_to_s = to_string(xd); // d_to_s = "1234.56789" string sub1 = str1.substr(5);// sub1 = "56789" string sub2 = str1.substr(5,3);// sub2 = "567" string sub3 = sub1 + sub2;// sub3 = "56789567" const char *ch = str1.c_str(); // ch = "1234.56789" cout << "str: " << str1 << "\t type: " << typeid(str1).name() << "\n"; cout << "str to int: " << x << "\t type: " << typeid(x).name() << "\n"; cout << "str to double: " << xd << "\t type: " << typeid(xd).name() << "\n"; cout << "substr one parameter: " << sub1 << "\t type: " << typeid(sub1).name() << "\n"; cout << "substr two parameter: " << sub2 << "\t type: " << typeid(sub2).name() << "\n"; cout << "string + operation: " << sub3 << "\t type: " << typeid(sub3).name() << "\n"; cout << "str to char array: " << ch << "\t type: " << typeid(ch).name() << "\n"; }
실행결과는 다음과 같다.
str: 1234.56789 $\quad$ type: std::_ _cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
str to int: 1234 $\quad$ type: int
str to double: 1234.56789 $\quad$ type: double
substr one parameter: 56789 $\quad$ type: std::_ _cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
substr two parameter: 567 $\quad$ type: std::_ _cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
string + operation: 56789789 $\quad$ type: std::_ _cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
str to char array: 1234.56789 $\quad$ type: char const*
g++의 경우 typeid를 나름대로 define하여 쓰고 있어 그냥 실행하면 int는 i로 double은 d로 const char* 는 PKc로 나타난다. 위와 같이 정확한 명칭으로 나타나게 하기 위해서는
./example_string | c++filt --types
형식으로 실행을 해야한다.
vector
vector는 다루기 쉬운 동적배열 자료형이다.
vector에서 지원하는 함수는 다음과 같다.
| 함수명 | 함수 설명 | |
|---|---|---|
| vector.begin() | vector의 첫 요소의 주소를 가리키는 포인터를 반환한다. | |
| vector.end() | vector의 마지막요소 +1 번째 요소(null값)의 주소를 가리키는 포인터를 반환한다. | |
| vector.capacity() | vector의 크기를 반환한다. | |
| vector.size() | vector가 실제로 사용하고 있는 크기를 반환한다. | |
| vector.at(x) | vector의 x번째 요소를 반환한다. 잘못된 위치를 넣으면 컴파일 타임에서 에러가 발생한다. | |
| vector.push_back(x) | vector의 마지막 요소 다음에 x를 추가한다. | |
| - | C++에 matplotlib 라이브러리 추가해서 그래프 그리기 | |
matplotlib 라이브러리 추가해서 그래프 그리기
이 문서는 Ubuntu-20.04 LTS 버전 기준으로 작성되었다.
- git clone을 이용하든 https://github.com/lava/matplotlib-cpp에 직접 접속하든 선택하여 matplotlibcpp.h파일을 /usr/include 혹은 /usr/local/include 에 넣는다.
- sudo apt install python-numpy를 터미널에서 실행한다.
다음은 간단히 sin 그래프를 그리는 예제코드다.
- | example.cpp
#include <iostream> #include <vector> #include <cmath> #include <matplotlibcpp.h> using std::vector; using std::sin; using std::cout; namespace plt = matplotlibcpp; int main() { const double pi = M_PI; vector<double> x(21); vector<double> y(21); for(int i = 1; i < x.capacity(); ++i) { x[i] = x[i-1] + pi/10; y[i] = sin(x[i]); } plt::plot(x,y); //plot the x,y plt::grid(true); //show grid plt::show(); // show figure }
컴파일할 때는
g++ example.cpp -I/usr/include/python3.8 -lpython3.8 -o example
로 하면 된다.
