Study/C++

[C++] std::array (C++ 표준 라이브러리 배열 컨테이너 이야기)

2022. 9. 18. 19:32
목차
  1. 생성자 (Initializer)
  2. 대입 연산자 (operator=)
  3. 접근자 (operator[ ], at, front, back)
  4. 반복자 (iterator => begin, cbegin, rbegin, crbegin, end, cend, rend, crend)
  5. 배열의 크기 (size)
  6. 배열이 비어있는 지 확인 (empty)
  7. 주요 기능 (fill, swap)
  8. 참고 자료

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;
}

실행 결과는 위와 같습니다.

 

참고 자료

 

std::array - cppreference.com

template<     class T,     std::size_t N > struct array; (since C++11) std::array is a container that encapsulates fixed size arrays. This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-s

en.cppreference.com

 

array 클래스(C++ 표준 라이브러리)

자세한 정보: array 클래스(C++ 표준 라이브러리)

learn.microsoft.com

 

저작자표시 비영리 변경금지 (새창열림)

'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
  • 생성자 (Initializer)
  • 대입 연산자 (operator=)
  • 접근자 (operator[ ], at, front, back)
  • 반복자 (iterator => begin, cbegin, rbegin, crbegin, end, cend, rend, crend)
  • 배열의 크기 (size)
  • 배열이 비어있는 지 확인 (empty)
  • 주요 기능 (fill, swap)
  • 참고 자료
'Study/C++' 카테고리의 다른 글
  • [C++11] 스마트 포인터2 (shared_ptr)
  • [C++11] 스마트 포인터1 (unique_ptr)
  • [C++] Google 공룡 게임 만들어보기
  • [C++] 데이터 직렬화 라이브러리 씨리얼(Cereal)
Eskeptor
Eskeptor
Eskeptor
Hello World
Eskeptor
전체
오늘
어제
  • 분류 전체보기 (138)
    • Computer (5)
      • Linux (1)
      • Hardware (2)
      • Software (0)
      • Tips (1)
      • Website (0)
    • Mobile (1)
      • Application (1)
    • Study (108)
      • Android (9)
      • C언어 (45)
      • C++ (17)
      • Unity 5(유니티5) (11)
      • Qt 프로그래밍 (2)
      • MFC (12)
      • C#, Winform (12)
    • My World (24)
      • OpenPad(Android) (12)
      • 한글 패치 (1)
      • C#으로 만든 귀요미들 (5)
      • MFC로 만든 귀요미들 (6)
    • Life Goes On (0)
      • Hip Hop (0)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • Java
  • C++
  • openpad
  • 메모장
  • 강의
  • 유니티
  • 테트리스
  • C언어
  • 안드로이드
  • Tetris
  • 비행기
  • 오픈패드
  • 왕초보
  • 프로그래밍
  • C#
  • 포인터
  • 알고리즘
  • 기본
  • 초보
  • 자바
  • 강좌
  • Unity
  • 만들기
  • 슈팅게임
  • 기초
  • 자료구조
  • c++11
  • 배열
  • MFC
  • Android

최근 댓글

최근 글

hELLO · Designed By 정상우.
Eskeptor
[C++] std::array (C++ 표준 라이브러리 배열 컨테이너 이야기)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.