#22. [모던 C++ STL] array(C++11)
개요
기존의 vector는 C스타일의 배열과 호환성이 좋았으나, 요소 추가/삭제등이 필요하여 동적 메모리를 사용하다 보니, 메모리 할당시 힙을 사용합니다. 이 때문에 아무래도 메모리 할당 측면에서는 스택에 할당되는 C스타일의 배열보다 성능이 떨어졌습니다.
C++11 부터는 array가 추가되어 기존 C스타일의 배열처럼 연속된 메모리를 사용하는 컨테이너를 제공합니다. C스타일 배열처럼 컴파일 타임에 크기가 결정되어 스택에 할당되므로, 힙에 할당되는 vector 보다 성능이 좋습니다.
항목 | 설명 | C스타일 배열 | array | vector |
---|---|---|---|---|
연속된 메모리 | 모두 연속된 메모리를 사용합니다. | O | O | O |
동적 메모리 할당 | C스타일 배열과 array는 컴타일 타임에 크기가 결정되어 스택에 할당됩니다. | X | X | O |
요소 추가/삭제 | C스타일 배열과 array는 요소를 추가/삭제할 수 없습니다. | X | X | O |
요소 접근 [] |
인덱스 범위 검사를 하지 않습니다. | O | O | O |
요소 접근 at() |
인덱스 범위 검사를 합니다. | X | O | O |
이터레이터 | array와 vector는 순방향 및 역방향 탐색을 지원합니다. | X | O | O |
배열 주소 | 데이터의 첫번째 요소의 주소를 구할 수 있습니다. | arr 또는 arr[0] |
arr.data() |
&v[0] |
포인터로의 암시적 변환 | C스타일 배열은 int* p = arr; 와 같이 포인터로 암시적 변환 됩니다.오버로딩된 함수 결정 규칙에 따라 함수 인자로 C스타일 배열 사용시 포인터로 취급됩니다. |
O | X | X |
중괄호 초기화를 통한 요소 갯수 유추 | 배열은 초기화 요소의 갯수로 C스타일 배열의 요소 갯수를 유추하지만, array는 명시적으로 지정해야 합니다. | O | X | O |
중괄호 초기화를 통한 자동 제로 초기화 | 요소의 갯수보다 적게 초기화하면 나머지 요소는 0 으로 초기화 됩니다. |
O | O | O |
속도 | C스타일 배열과 array가 스택에 할당되기 때문에, 힙에 할당되는 vector보다 빠릅니다. | 1 | 2 | 3 |
대입 | C스타일 배열은 대입을 지원하지 않으며, array는 요소의 갯수가 같으면 대입됩니다. | X | O | O |
1
2
3
4
5
6
7
8
9
10
11
12
std::array<int, 3> a{1, 2, 3};
std::array<int, 3> b{4, 5};
std::array<int, 2> c{4, 5};
// int* ptr = a; // (X) 컴파일 오류. array는 포인터로 암시적 변환이 되지 않습니다.
int* ptr = a.data(); // data를 이용하면 포인터로 변환됩니다.
EXPECT_TRUE(b[0] == 4 && b[1] == 5 && b[2] == 0); // 요소의 갯수보다 적게 초기화하면 나머지 요소는 0으로 초기화 됩니다.
// c = a; // (X) 컴파일 오류. 요소의 갯수가 다르면 컴파일 오류가 발생합니다.
b = a; // 요소의 갯수가 같으면 대입이 가능합니다.
EXPECT_TRUE(b[0] == 1 && b[1] == 2 && b[2] == 3);
array 멤버 함수
항목 | 내용 |
---|---|
at() (C++11~) |
(작성중) |
front() (C++11~) |
(작성중) |
back() (C++11~) |
(작성중) |
data() (C++11~) |
컨테이너가 관리하는 메모리 블록을 리턴합니다. |
begin(), end() (C++11~) |
(작성중) |
cbegin(), cend() (C++11~) |
(작성중) |
rbegin(), rend() (C++11~) |
(작성중) |
crbegin(), crend() (C++11~) |
(작성중) |
empty() (C++11~) |
(작성중) |
size() (C++11~) |
(작성중) |
max_size() (C++11~) |
(작성중) |
fill() (C++11~) |
(작성중) |
swap() (C++11~) |
(작성중) |
== (C++11~)!= (C++11~C++20) |
(작성중) |
<, <=, >, >= (C++11~C++20)<=> (C++20~) |
(작성중) |
to_array() (C++20~) | 기존 C스타일 배열로부터 array를 생성합니다. |
배열, array, vector 속도 비교
다음은 C스타일 배열, array, vector의 생성/소멸과 접근 속도를 테스트한 예입니다. 시간 측정을 위해 chrono 라이브러리를 사용했습니다.
- 십만개의 요소를 십만번 생성/소멸했고,
- 십만개의 요소에 접근하여 값을 대입합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 생성/소멸 테스트
void CStyleArrayConstruct() {
for(int i{0}; i < 100000; ++i) {
int arr[100000];
}
}
void STLArrayConstruct() {
for(int i{0}; i < 100000; ++i) {
std::array<int, 100000> arr;
}
}
void STLVectorConstruct() {
for(int i{0}; i < 100000; ++i) {
std::vector<int> arr(100000);
}
}
// 접근 테스트
void CStyleArrayAccess() {
int arr[100000];
for(int i{0}; i < 100000; ++i) {
arr[i] = i;
}
}
void STLArrayAccess() {
std::array<int, 100000> arr;
for(int i{0}; i < 100000; ++i) {
arr[i] = i;
}
}
void STLVectorAccess() {
std::vector<int> arr(100000);
for(int i{0}; i < 100000; ++i) {
arr[i] = i;
}
}
template<typename Func>
std::chrono::microseconds Measure(Func f) {
std::chrono::system_clock::time_point start{std::chrono::system_clock::now()};
f();
std::chrono::system_clock::time_point end{std::chrono::system_clock::now()};
std::chrono::microseconds val{std::chrono::duration_cast<std::chrono::microseconds>(end - start)};
return val;
}
// 생성/소멸 테스트
std::cout << "CStyleArray : " << Measure(CStyleArrayConstruct).count() << std::endl;
std::cout << "STLArray : " << Measure(STLArrayConstruct).count() << std::endl;
std::cout << "STLVector : " << Measure(STLVectorConstruct).count() << std::endl;
// 접근 테스트
std::cout << "CStyleArray : " << Measure(CStyleArrayAccess).count() << std::endl;
std::cout << "STLArray : " << Measure(STLArrayAccess).count() << std::endl;
std::cout << "STLVector : " << Measure(STLVectorAccess).count() << std::endl;
실행 결과는 다음과 같습니다. array가 생성/소멸이 C스타일 배열 보다 빠른게 좀 의외이긴 합니다.
1
2
3
4
5
6
7
CStyleArray : 660
STLArray : 228 // C스타일 배열보다 빠릅니다.
STLVector : 18612352 // 동적 할당을 하다보니 느립니다.
CStyleArray : 209
STLArray : 404 // operator[]을 이용하다 보니 C스타일 배열보다는 느립니다.
STLVector : 487 // operator[]을 이용하다 보니 C스타일 배열보다는 느립니다.
댓글남기기