ShovelingLife
A Game Programmer
ShovelingLife
전체 방문자
오늘
어제
  • 분류 전체보기 (1074)
    • 그래픽스 (57)
      • 공통 (19)
      • 수학 물리 (22)
      • OpenGL & Vulkan (1)
      • DirectX (14)
    • 게임엔진 (183)
      • Unreal (69)
      • Unity (103)
      • Cocos2D-X (3)
      • 개인 플젝 (8)
    • 코딩테스트 (221)
      • 공통 (7)
      • 프로그래머스 (22)
      • 백준 (162)
      • LeetCode (19)
      • HackerRank (2)
      • 코딩테스트 알고리즘 (8)
    • CS (235)
      • 공통 (21)
      • 네트워크 (44)
      • OS & 하드웨어 (55)
      • 자료구조 & 알고리즘 (98)
      • 디자인패턴 (6)
      • UML (4)
      • 데이터베이스 (7)
    • 프로그래밍 언어 (349)
      • C++ (168)
      • C# (90)
      • Java (9)
      • Python (33)
      • SQL (30)
      • JavaScript (8)
      • React (7)
    • 그 외 (10)
      • Math (5)
      • 일상 (5)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • Source Code 좌측 상단에 복사 버튼 추가 완료
  • 언리얼 엔진 C++ 빌드시간 단축 꿀팁
  • 게임 업계 코딩테스트 관련
  • 1인칭 시점으로 써내려가는 글들

인기 글

태그

  • C
  • 유니티
  • 언리얼
  • 클래스
  • 오블완
  • c#
  • 배열
  • C++
  • 파이썬
  • 티스토리챌린지
  • 문자열
  • 그래픽스
  • SQL
  • 함수
  • 백준
  • string
  • 포인터
  • 알고리즘
  • 프로그래머스
  • Unity

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
ShovelingLife

A Game Programmer

C++ vector사용법 및 설명 (장&단점)
CS/자료구조 & 알고리즘

C++ vector사용법 및 설명 (장&단점)

2022. 8. 3. 19:58
vector<Type> vect; // vect는 stack, 각 원소들은 heap
vector<Type> *vect = new vector<Type>; // vect와 각 원소들 모두 heap
vector<Type*> vect; // vect는 stack, 각 원소들은 heap 그리고 가리키는 대상들은 heap 또는 stack

https://stackoverflow.com/questions/8036474/when-vectors-are-allocated-do-they-use-memory-on-the-heap-or-the-stack

 

속도적인 측면에서 array(배열)에 비해 성능은 떨어지지만 메모리를 효율적으로 관리하고 예외처리가 쉽다는 장점이 있다.

Vector의 초기화

 vector<자료형> 변수명 백터 생성 
 vector<자료형> 변수명(숫자) 숫자만큼 백터 생성 후 0으로 초기화 
 vector<자료형> 변수명 = { 변수1, 변수2, 변수3... } 백터 생성 후 오른쪽 변수 값으로 초기화 
 vector<자료형> 변수명[] = {, }  백터 배열(2차원 백터)선언 및 초기화(열은 고정, 행은 가변)
 vector<vector<자료형> 변수명  2차원 백터 생성(열과 행 모두 가변)
 vector<자료형>변수명.assign(범위, 초기화할 값)  백터의 범위 내에서 해당 값으로 초기화
vector<int> v; //int형 백터 생성
vector<int>v(4); //int형 백터 생성 후 크기를 4로 할당(모든 백터요소 0으로 초기화)
vector<int>v = { 1, 2, 3 }; //int형 백터 생성 후 1, 2, 3 으로 초기화
vector<int>v[] = { { 1, 2}, {3, 4} }; //int형 백터 배열 생성(행은 가변이지만 열은 고정)
vector<vector<int>> v; //2차원 백터 생성(행과 열 모두 가변)
vector<int> v = { 1, 2, 3, 4, 5 }; //백터 범위를 5로 지정하고 정수 10으로 초기화
v.assign(5, 10); //output : 10 10 10 10 10

Vector의 Iterators

 v.begin()  백터 시작점의 주소 값 반환
 v.end()   백터 (끝부분 + 1) 주소값 반환
 v.rbegin() (revers begin)   백터의 끝 지점을 시작점으로 반환 
 v.rend() (revers end)  백터의 (시작 + 1) 지점을 끝 부분으로 반환 

begin(), end() 그림

rbegin(), rend() 그림

#include <iostream>
#include <algorithm>
#include <array>
#include <vector>

using namespace std;

int main()
{
    vector<int> v = { 1, 2, 3, 4 };

    for_each(v.begin(), v.end(), [&](int& n)
        {
            cout << n << endl;        //output : 1 2 3 4
        });

    for_each(v.rbegin(), v.rend(), [&](int& n)
        {
            cout << n << endl;        //output : 4 3 2 1
        });

    for (auto itor = v.begin(); itor != v.end(); itor++)
        cout << *itor << endl;        //output : 1 2 3 4

    for (auto itor2 = v.rbegin(); itor2 != v.rend(); itor2++)
        cout << *itor2 << endl;        //output : 4 3 2 1
}

Vector Element access(백터의 요소 접근)

 v.at(i)   백터의 i번째 요소 접근 (범위 검사함)
 v.[i] (operator [])   백터의 i번째 요소 접근 (범위 검사 안함) 
 v.front()   백터의 첫번째 요소 접근 
 v.back()   백터의 마지막 요소 접근 

※vector의 at과 []에 대한 차이점을 설명하자면 다음과 같다.

둘다 똑같이 N번째의 요소 접근이지만 at은 범위를 검사하여 범위 밖의 요소에 접근 시 예외처리를 발생시킵니다. (std::out_of_range) 하지만 [](operator [])는 범위검사를 하지 않으며 예외처리를 발생시키지 않는다. 또한 해당범위 밖의 요소에 접근을 시도한다면 일반적인 디버깅(g++ or VC++)이 발생한다. 백터는 효율을 중점으로 둔 라이브러리 함수여서 보통 []를 권장하고 있으며 여러가지 방법으로 유효성 검사가 가능하기 때문에 []를 많이 사용하고 있다.

vector<int> v = { 1, 2, 3, 4};

cout << v.front() << endl;        //output : 1
cout << v.back() << endl;        //output : 4
cout << v.at(1) << endl;        //output : 2
cout << v[2] << endl;            //output : 3​

Vector에 요소 삽입

 v.push_back()   백터의 마지막 부분에 새로운 요소 추가 
 v.pop_back()  백터의 마지막 부분 제거 
 v.insert(삽입할 위치의 주소 값, 변수 값)  사용자가 원하는 위치에 요소 삽입 
 v.emplace(삽입할 위치의 주소 값, 변수 값)    사용자가 원하는 위치에 요소 삽입(move로 인해 복사생성자 X) 
 v.emplace_back()  백터의 마지막 부분에 새로운 요소 추가(move로 인해 복사생성자 X) 
 v.erase(삭제할 위치) or v.erase(시작위치, 끝위치)  사용자가 원하는 index값의 요소를 지운다.
 v.clear()  백터의 모든 요소를 지운다.(return size = 0)
 v.resize(수정 값)  백터의 사이즈를 조정한다.(범위 초과시 0으로 초기화) 
 v.swap(백터 변수)  백터와 백터를 스왑한다. 
#include <vector>

int main(){
    vector<int> v;

    v.push_back(10);
    v.push_back(20);        //v = { 10, 20 }

    v.inset(v.begin() + 1, 100);     // v = { 10, 100, 20 }

    v.pop_back();        // v = { 10, 100 }

    v.emplace_back(1); //v = { 10, 100, 1 }
    v.emplace_back(2);    //v = { 10, 100, 1, 2 }
    v.emplace(v.begin() + 2, -50);    //v = { 1, 100, -50, 1, 2 }

    v.erase(v.begin() + 1); // v = { 1, -50, 1, 2 }
    v.resize(6);    // v = { 1, -50, 1, 2, 0, 0 }
    v.clear();    // v = empty()     
}

기본적으로 push_back() 함수는 값을 넣는 과정에서 복사생성자를 호출하게 된다. 뿐만 아니라 insert를 하게 된다면 모든 값들을 새로운 메모리에 복사한 후 해당 자리에 값을 넣게 된다. (일반적인 배열 중간에 값을 끼워 넣는거랑 똑같다.)

이렇게 되버리면 복사생성자로 인한 오버헤드가 커지게 되며 성능 저하를 야기함으로 대신 emplace와 emplace_back은 백터 내부에서 값들을 생성하는 걸(생성자만 호출) 사용하자.

 

vector의 크기에는 size()와 capacity() 두개가 있는데, size()는 백터가 생성된 크기이며 capacity()는 백터의 최대 할당 크기다.

위 사진처럼 되어 있는데 백터의 크기가 capacity()의 크기를 초과해 버린다면 reallocate(재할당)이 발생한다. 재할당이 발생한다면 모든 값들을 새로운 메모리 공간에 복사한 후 기존 백터를 파괴해 버린다. 여기서 복사 과정에서도 복사생성자가 발생하게 됨으로서 프로그램의 퍼포먼스가 저하된다. 또한 reallocate이 자주 일어나는 현상을 막기 위해서 reserve()라는 함수를 사용해주면 되는데 이 함수는 백터의 capacity() 크기를 설정해주는 함수로 충분한 백터를 만들어서 불필요한 생성과정을 없애주는 역할을 한다. 하지만 reserve()를 너무 크게 잡게되면 백터가 불필요하게 늘어나 메모리를 많이 잡아먹을 수 있다. 따라서 남은 공간을 잡아주는 함수가 바로 shrink_to_fit()이라는 함수를 통해 capacity()의 크기를 조정해 줄 수 있다.

 

clear()로 백터의 값들을 지우게 되면 백터의 요소들은 없어지지만 capacity()는 남아있다. 즉, clear()로 백터의 값들을 지운 후 다시 사용하지 않는다면 해당 백터의 메모리 공간은 잉여로 남게 된다. 이걸 해결할 수 있는 방법이 swap을 이용한 방법인데, 사용법은 다음과 같다.

vector<int> v = { 1, 2, 3, 4};
v.clear();
cout << v.capacity() << endl;    //output : 10

vector<int>().swap(v);
cout << v.capacity() << endl;    //output : 0​

이렇게 하면 아무것도 없는 백터공간과 swap이 일어나 capacity() 공간을 없앨 수 있다. 하지만 함수가 끝나면(중괄호를 벗어나면) 자동으로 힙에서 메모리 해제가 된다. insert()와 erase() 해당 함수를 사용 시 변수를 찾은 후 해당 공간을 만들기 위해 데이터를 일일이 이동시킨다. 만약 capacity()값을 초과될 경우 reallocate도 일어나게 되므로 메모리 오버헤드 및 성능저하도 야기할 수 있다. 따라서 삽입과 삭제가 빈번히 일어날 경우 vector보단 list나 deque를 사용하는 것이 바람직하다.

Vector Capacity(백터 용량)

 v.empty()  백터가 빈공간이면 true, 값이 있다면 false 
 v.size()   백터의 크기 반환 
 v.capacity()  heap에 할당된 백터의 실제크기(최대크기) 반환 
 v.max_size()  백터가 system에서 만들어 질 수 있는 최대 크기 반환 
 v.reserve(숫자)  백터의 크기 설정 
 v.shrink_to_fit()  capacity의 크기를 백터의 실제 크기에 맞춤 
vector<int>v = { 1, 2, 3, 4 };

cout << v.size() << endl;    //output : 4
cout << v.capacity() << endl; //output : 10 (컴파일 환경에 따라 달라질 수 있음)

v.reserve(6);
cout << v.capacity() << endl; //output : 6
cout << v.max_size() << endl; //output : 1073741823(시스템 성능에 따라 달라질 수 있음)

v.shrink_to_fit();
cout << v.capacity() << endl; //output : 4

cout << v.empty() << endl; //output : false
v.clear();
cout << v.empty() << endl; //output : true​

출처 : https://hwan-shell.tistory.com/119

저작자표시 (새창열림)

'CS > 자료구조 & 알고리즘' 카테고리의 다른 글

가중치 그래프와 임계 경로(Critical Path)  (0) 2022.08.14
C++ vector empty()와 size()간 차이  (0) 2022.08.04
C 정렬 코드  (0) 2022.07.20
컨테이너 어댑터 (스택, 큐, 우선순위 큐)  (0) 2022.07.04
[C++] Deque 데크  (0) 2022.07.04
    'CS/자료구조 & 알고리즘' 카테고리의 다른 글
    • 가중치 그래프와 임계 경로(Critical Path)
    • C++ vector empty()와 size()간 차이
    • C 정렬 코드
    • 컨테이너 어댑터 (스택, 큐, 우선순위 큐)
    ShovelingLife
    ShovelingLife
    Main skill stack => Unity C# / Unreal C++ Studying Front / BackEnd, Java Python

    티스토리툴바