C++ 표준 라이브러리에 있는 고정된 크기의 배열을 캡슐화 한 컨테이너(container that encapsulates fixed size arrays.)
입니다.
C++을 사용하다 보면 일반 배열을 사용하기 보다는 std::array
를 더 많이 사용하기도 합니다.
(도입된 표준은 C++11
에서 도입되었습니다. 그렇기 때문에 C++11
이하의 환경에서 개발을 할때는 사용하지 못합니다.)
자주 사용되는 몇 가지를 보도록 합시다.
생성자 (Initializer)
기본적으로 Aggregate 한 방식으로 사용됩니다.
#include <array> // 헤더파일 필요
// 사용법
std::array<자료형, 배열크기(무조건 필요)> 이름 = 초기화값;
// Aggregate 형식 지원
std::array<int, 3> arrInt1 = {1,2,3};
// 크기는 정해졌으나 데이터 초기화 X
std::array<int, 5> arrInt2;
이때 주의점은 무조건 초기 선언시 자료형과 배열 크기가 필수
라는 것입니다.
대입 연산자 (operator=)
std::array
는 대입 연산자를 통해서 전체 배열 데이터를 변경할 수 있습니다.
(std::array
의 operator=의 반환 값은 *this
입니다.)
#include <iostream>
#include <array>
int main(void)
{
std::array<int, 3> intArr1 = { 1, 2, 3 };
std::array<int, 3> intArr2 = intArr1;
std::cout << "intArr1 : ";
for (const int& i : intArr1)
std::cout << i << " ";
std::cout << std::endl;
std::cout << "intArr2 : ";
for (const int& i : intArr2)
std::cout << i << " ";
std::cout << std::endl;
return 0;
}
결과는 위와 같습니다.
하지만 같은 주소를 공유하는 것은 아니고
위와 같이 intArr1은 0x1411bf958
이고 intArr2는 0x1411bf988
가 되듯 자기만의 주소를 가지고 있습니다.
(값만 복사)
그래서 일반 배열처럼 memcpy
를 안해도 됩니다.
접근자 (operator[ ], at, front, back)
std::array
에서 접근자는 크게 operator[ ]
와 at
을 사용합니다.
또한 맨 앞의 요소에 접근할 때는 front
, 맨 뒤의 요소에 접근할 때는 back
을 사용합니다.
operator[ ]
의 경우에는 일반 배열에서 사용하듯이 빠르게 해당 인덱스에 접근
할 수 있지만 배열에서도 그랬듯이 경계 범위 체크(bounds checking)
를 하지 않아 Out Of Range와 같은 비정상 접근 오류
를 범할 수 있습니다.
at
의 경우에는 경계 범위 체크(bounds checking)
를 거친 후에 해당 값에 접근하게 됩니다.
이때 범위에 벗어나게 되면 예외(std::out_of_range)
를 던집니다.
#include <iostream>
#include <array>
int main(void)
{
std::array<int, 3> intArr1 = { 1, 2, 3 };
std::cout << "intArr1[0] : " << intArr1[0] << std::endl;
std::cout << "intArr1.at(1) : " << intArr1.at(1) << std::endl;
try
{
std::cout << "intArr1.at(4) : " << intArr1.at(4) << std::endl;
}
catch (const std::out_of_range& ex)
{
std::cout << "intArr1.at(4) : " << ex.what() << std::endl;
}
return 0;
}
결과는 위와 같습니다.
반복자 (iterator => begin, cbegin, rbegin, crbegin, end, cend, rend, crend)
반복자는 크게 r이 붙은 반복자 rbegin, crbegin, rend, crend
와
r이 안붙은 반복자 begin, cbegin, end, cend
가 있습니다.
그런데 여기에 c가 붙은 반복자 cbegin, cend, crbegin, crend
가 보이는데 단순히 설명하자면 앞에 const
가 붙어서 해당 데이터를 수정할 수 없다는 점이 있습니다.
r이 붙은 반복자는 reverse
라고 생각하면 됩니다.
// begin 과 end, rbegin과 rend 예제
#include <iostream>
#include <array>
int main(void)
{
std::array<int, 5> intArr = { 1, 2, 3, 4, 5 };
auto iter = intArr.begin();
while (iter != intArr.end())
{
// 홀수인 경우에 10을 더해주기
if (*iter & 1)
{
*iter += 10;
}
// 데이터에 접근할 때는 앞에 애스터리스크(*)를 붙인다.
std::cout << *iter << " ";
// begin(1) -> 2 -> 3 -> 4 -> end(5) 순서로 증가
iter++;
}
// 위의 과정이 끝나면 intArr은 { 11, 2, 13, 4, 15 }
std::cout << std::endl;
auto riter = intArr.rbegin();
while (riter != intArr.rend())
{
// 홀수인 경우에 10을 뺴주기
if (*riter & 1)
{
*riter -= 10;
}
// 데이터에 접근할 때는 앞에 애스터리스크(*)를 붙인다.
std::cout << *riter << " ";
// begin(5) -> 4 -> 3 -> 2 -> end(1) 순서로 증가
riter++;
}
// 위의 과정이 끝나면 intArr은 { 1, 2, 3, 4, 5 }
return 0;
}
결과는 위와 같습니다.
실제로 디버깅을 해보면
첫번째 while문
이 끝나면 홀수 부분의 값들에 10씩 더한 값들이 들어간 것을 볼 수 있습니다.
두번째 while문
이 끝나면 홀수 부분의 값들에 10씩 뺀 값들이 들어간 것을 볼 수 있습니다.
위의 예제에서 begin
을 cbegin
으로, rbegin
을 crbegin
으로 변경하면 값을 변경할 수 없다고 뜨게 됩니다.
이로서 c가 들어간 반복자는 단순하게 값을 반환하여 사용할때만 사용하여 사용자의 실수를 허용하지 않게 할 수 있습니다.
(값을 모르고 변경하는 일이 없도록)
배열의 크기 (size)
size
는 현재 std::array
의 크기를 반환하는 함수 입니다.
#include <iostream>
#include <array>
int main(void)
{
std::array<int, 5> intArr = { 1, 2, 3, 4, 5 };
for (int i = 0; i < intArr.size(); i++)
{
std::cout << intArr[i] << " ";
}
std::cout << std::endl;
return 0;
}
주로 for문
과 함께 사용합니다.
배열이 비어있는 지 확인 (empty)
현재 std::array
가 비어있는 지 확인하는 함수입니다.
여기서 함정이 있는데 일반적으로는 std::array
는 empty
가 될 수 없습니다.
#include <iostream>
#include <array>
int main(void)
{
std::array<int, 5> intArr1;
std::array<int, 0> intArr2;
std::cout << "intArr1 : " << intArr1.empty() << std::endl;
std::cout << "intArr2 : " << intArr2.empty() << std::endl;
std::cout << std::endl;
return 0;
}
그 이유는 일반적으로는 크기를 0으로 선언해야만 empty
의 값이 true
가 되기 때문이죠.
주요 기능 (fill, swap)
fill
은 모든 데이터의 값을 변경하는 기능을 하고,
swap
은 모든 데이터를 다른 std::array
데이터와 교환하는 기능을 합니다.
(swap의 경우에는 swap하는 상대끼리의 크기가 같아야 합니다.)
#include <iostream>
#include <array>
#include <algorithm>
int main(void)
{
std::array<int, 5> intArr1; // 선언만 했기 때문에 쓰레기값이 들어있음
std::for_each(intArr1.begin(), intArr1.end(), [](int& num) { std::cout << num << " "; });
std::cout << std::endl;
intArr1.fill(0); // 모든 데이터를 0으로 초기화
std::for_each(intArr1.begin(), intArr1.end(), [](int& num) { std::cout << num << " "; });
std::cout << std::endl;
std::array<int, 5> intArr2 = { 10,20,30,40,50 };
intArr1.swap(intArr2); // intArr1과 intArr2의 데이터를 Swap
std::cout << "intArr1 : ";
std::for_each(intArr1.begin(), intArr1.end(), [](int& num) { std::cout << num << " "; });
std::cout << std::endl;
std::cout << "intArr2 : ";
std::for_each(intArr2.begin(), intArr2.end(), [](int& num) { std::cout << num << " "; });
std::cout << std::endl << std::endl;
return 0;
}
실행 결과는 위와 같습니다.
참고 자료
'Study > C++' 카테고리의 다른 글
[C++11] 스마트 포인터3 (weak_ptr) (0) | 2022.09.21 |
---|---|
[C++11] 스마트 포인터2 (shared_ptr) (1) | 2022.09.20 |
[C++11] 스마트 포인터1 (unique_ptr) (0) | 2022.09.19 |
[C++] Google 공룡 게임 만들어보기 (2) | 2022.02.05 |
[C++] 데이터 직렬화 라이브러리 씨리얼(Cereal) (0) | 2021.03.25 |