반응형

C++11 : STL std::array 정적배열

배경

 C++11에서 등장한 array는 기존의 정적 배열(이제는 C Style array 라고 불림)을 개선하기 위해서 등장을 했다. C/C++ 외의 다른 프로그래밍 언어를 조금 훝어보면 알 수 있다.


 JAVA, JS, C# 등에서의 배열을 선언/정의 할때 부가 정보(Length 등)가 같이 있는 것에 비해서 C++11이전의 C Style static array은 단순히 연속된 메모리 공간의 앞부분을 가르키는 포인터에 불과 했었다. 이로 포인터를 사용할 경우 잘못된 메모리 참조 또는 이를 개선하기 위한 추가 코딩으로 인한 생산성 저하로 이어지는 문제가 있다.


 그래서 C++11이전에서는 std::vector를 사용하기도 했다. 그러나 C Style Array에 비해서 성능 저하의 부담이 크며, 오버플로워가 일어나면, 동적으로 용량을 늘려 버리는 바람에 정해진 크기를 버퍼로 사용하기에는 잠재적인 문제가 있을 수 있다(물론 좋은 프로그래머는 이런 문제를 예방을 할 것이다).


 std::array는 vector보다 경량이며, 기존의 정적 배열의 성질을 가졌으며, 반복자등 일부 STL 컨테이너를 사용할 수 있게 만들어졌다.



선언 및 초기화 방법

 array표준헤더파일을 포함해야 사용할 수 있다. 타입과 배열크기를 입력하고 기존 컨테이너처럼 선언하고 사용할 수 있다. <>에 첫인자는 자료형(type), 두번째 인자는 크기를 입력해야 한다.

#include <array>

int main(const int argc, const char * argv[])
{
    std::array<int, 5> arr1 = {1, 2, 3};
    std::array<int, 5> arr2(1, 2, 3);
    std::array<int, 5> arr3{1, 2, 3};

    return 0;
}


주요 특징

 정의된 array의 남는 원소에는 0으로 초기화가 된다.

 array도 vector와 같이 객체명(혹은 변수명) 뒤에 대괄호로 배열처럼 사용할 수 있다.

 vector처럼 반복자를 사용할 수 있다.


예시

// arrayEx.cpp
#include <array>
#include <iostream>

int main(cosnt int argc, const char * argv[])
{
    std::array<int, 5> arr1{1, 2, 3};
    // 네번째 int에 접근하여 값을 대입함
    arr1[3] = 4;
    // 반복자처럼 사용 가능
    auto itArr = arr1.begin();
    for(; itArr != arr1.end(); ++itArr)
    {
        std::cout << (*itArr) << std::endl;
    }
    // 출력결과 arr1[4](5번째)는 0으로 초기화 되어 있음
    return 0;
}


 size() 함수로 배열의 크기를 알 수 있다. 단, capacity()가 없다. 이는 vector와 가장 큰 차이 중 하나다. 이는 array경우 size()와 capacity()가 따로 있을 필요가 없기 때문이다.

 array로 선언된 객체명은 array라는 컨테이너의 포인터이기이다. 이 때문에 기존의 정적 배열의 첫번째 포인터가 필요할 경우 vector와 같이 data()를 호출해서 사용해야 한다. 이는 오래된 라이브러리를 호환해야 한다던가 버퍼로 사용하기에 적절하다.

 fill()로 특정 원소값으로 모든 원소를 초기화 할 수 있다.


이 밖의 자세한 특징은 cpprefernce 문서를 참조하면 된다.



참조자료

cppReference 문서




반응형
반응형

STL C++ : vector 중복원소 제거

알게된 배경

 한대의 관리 PC에서 여러대의 디바이스 리눅스 계열 운영체제에 접속하는 디바이스 IP 리스트를 관리하기 위해서는 중복으로 접속을 하면 안되기 때문에 중복 검사하는 알고리즘이 필요 하게 되었었다. 일단 이 디바이스 정보들은 vector에 읽어놓은 상태이다(list를 사용하지 않은 이유는 일반적으로 디바이스 정보는 초기에만 설정을 해주고 나머지는 주로 읽기 때문에 이러한 구성을 선택하였다).

 기존의 수많은 STL 관련 서적들에서는 주로 숫자로 된 원소만 정렬하기 때문에 과연 실무에서 응용이 될지 의문이 들었었는데, 프로젝트를 하다 보니 응용을 하게 되어 다시 정리하게 되었다.


디바이스 리스트의 중복 검사

 일반적인 방법은 디바이스 정보중에 구분이 가능한 유니크(unique)한 요소로 비교하여 중복이 될 경우에는 바로 삭제하면 좋겠지만, vector에서 비교 도중에 삭제할 경우 index가 변경되는등 예상지 못하는 상황이 발생된다(그리고 코드도 복잡해진다).


 때문에 안전한 방법 생각한것은 다음과 같다.


1. 중복 검사중에는 중복으로 판된된 vector의 index만 따로 뽑아서 다른 vector에 저장을 해놓는다.


 이부분은 상황에 따라서 로직을 만들면 된다. 여기서는 잘 알려진 방법인 버블 정렬 하는 방식으로 비교하되 같은지 여부를 체크한다. 이 경우 2개의 중복이 있을 경우 문제가 안되지만, 3개 이상의 중복 원소가 있을 경우에는 문제가 생긴다. 때문에 이를 보완하기 위해서 2, 3단계들을 추가로 진행하는 것이다.


2. 중복 index가 있는 vector는 int같은 숫자 원소를 갖고 있기 때문에 (대부분 STL 교재의 예제에 있는)일반적으로 중복 검사 알고리즘으로 제거를 한다.


3. 중복 검사가 끝난 index들을 재 정렬을 한다.


4. 3에서 정렬한 vector의 index를 읽어서 제거를 한다. 이때 index가 큰 순서대로(배열의 뒤에서 부터) 제거를 한다.


 vector가 뒤에서 줄어들기 때문에 index가 바뀌는 일도 없고, 데이터 삭제시 이동되는 위치가 다르기 때문에 연산량도 줄어들게 된다.


STL vector 중복 제거

#include <vector>
#include <algorithm>
#include <string>

using namespace std;

struct deviceinfo
{
    string hostname;
    // 기타 요소
};

vector<int> checkOverlap(const vector<deviceinfo> &devVec)
{
    // 중복되는 원소가 보이면, 해당 index를 indexVec에 넣어주는 알고리즘을 구현한다.
    // 여기서는 devVec의 구조체 안에 hostname이 string을 비교한다고 가정한다.
    vector<int> indexVec;
    auto it = devVec.begin();    // auto는 C++11이상에서 사용
    for(int i=0; i<devVec.size(); ++i)
    {
        for(int j=i+1; j<devVec.size(); ++j)
        {
            if(0 == it[i].hostname.compare(it[j].hostname))
                indexVec.push_back(j);
        }
    }
    return indexVec;
}

void rmOverlap(vector<deviceinfo> &devVec)
{
    vector<int> indexVec = checkOverlap(devVec);

    // index중에서 중복되는 부분을 제거
    sort(indexVec.begin(), indexVec.end(), less<int>() );
    vector<int>::iterator pos;
    pos = unique(indexVec.begin(), indexVec.end() );
    indexVec.erase(pos, indexVec.end() );

    // 중복이 제거된 index를 내림차순으로 정렬
    sort(indexVec.begin(), indexVec.end(), less<int>() );
    // 디바이스 vector에서 중복 index부분을 제거
    pos = indexVec.begin();
    for(; pos != indexVec.end(); ++pos)
    {
        devVec.erase(devVec.begin() + (*pos));
    }
}

일반적으로 알려있는 부분은 이미 다른 사람(과 교재)에서 많이 검중이 되었기 때문에 이를 응용할 경우 비교적 안전하다. 중간에 삭제 삽입이 쉬운 리스트로 중복 검사를 한뒤에 vector로 옮겨서 사용하는 방법도 방법중 하나가 된다.


참고자료

블로그: 중복제거에 대해 간략히 나옴(설명은 없음)


추가수정(2017.6.7)

반응형

+ Recent posts