본문 바로가기

별걸다하는 IT/기타IT

[자료구조STL vector 1탄]벡터란? 배열 vs 벡터 비교/장단점/ 특징, 다양한 백터 선언 및 초기화 방법, 벡터 복사, 벡터 연산자 및 대소비교

안녕하세요

오늘은 C++ 자료구조 컨테이너 중 하나인 vector 라이브러리에 대해 살펴봅시다.

 

[1탄 목차]

1. 벡터란 무엇인가?

2. 벡터의 구조와 특징 (장단점)

3. 언제 벡터를 사용하는가

4. 벡터를 사용하기 위한 헤더

5. 벡터 다양한 선언 및 초기화

6. 벡터 복사하기

7. 가능한 연산자 대소비교 

 

[2탄 목차]

1. 벡터 멤버변수

2. 벡터 멤버함수

3. 벡터 사용법

4. 배열 <-> 벡터 변환법

5. 리스트 <-> 벡터 변환법 

1. 벡터란 무엇인가?

벡터는 어찌보면 배열입니다. 배열이랑 매우매우 유사해요. 근데 왜 벡터를 쓰느냐, 배열은 한 번 정해지면 고정이라 수정하기가 어려운데 벡터는 이를 쉽게 하기 위해서 만든 '동적 배열구조 클래스'이기 때문입니다.

출처: https://www.educba.com/c-plus-plus-vector-vs-array/

배열은 크기를 10으로 선언하면 10이 최대예요. 방을 늘릴수가 없어요! 그래서 초기에 넉넉히 공간을 잡아줘야 합니다. 

근데 벡터는 내가 초반에 10개의 방을 만들었어도 나중에 필요에 따라서 추가하거나 줄일 수 있답니다. (클래스로 구현되어있기 때문이예요)

 

배열에 관한 좀더 자세한 포스팅을 참조하고 싶으면 아래 포스팅을 참조해주세요.

 

[Java, C, C++ ] 배열이란, 배열 선언 및 초기화 - 프로그래밍기초

[Java, C, C++ 프로그래밍 완전정복 목차] 오늘 포스팅: 배열 (Array) 기초 항상 프로그래밍 포스팅은 무엇을 할까 고민되는 것 같아요 ㅎㅎ 배열도 워낙 무궁무진해서리... 범위를 우케 나눠야 나중에 꼬이지 않..

jhnyang.tistory.com

2. 벡터의 구조와 특징

단점 - [중간 삽입 삭제가 많은 상황에선 비효율적]

벡터는 배열이므로 배열과 매~~우 유사합니다. 배열처럼 데이터를 주소시작점부터 차곡차곡 담아냅니다. 

따라서 중간에 값을 삽입하거나 삭제하기 어려워요. (뭐 배열도 마찬가지) 주소 값을 다 이동해야하기 때문이죠. 하나씩 땡겨주거나 하나씩 밀어주거나.. 

출처: http://myblog.opendocs.co.kr/archives/1347

위 사진처럼 0번지 주소부터 차곡차곡 ABCDEFG가 저장되어 있다고 합시다. 그런데 내가 중간에 D를 삭제하고 싶어!! 그럼 index [3]에 해당하는 부분이 비게 되죠. 그러면 E,F,G를 앞으로 당겨줘야 해요. 이게 지금은 G까지밖에 없으니까 다행이지 100개 있는데 두번째 값을 삭제했다고 상상해봅시다. 그럼 뒤에 3부터 100까지 모두 한칸씩 앞으로 당겨줘야 하죠??

넵 결론적으로 삽입 삭제가 빈번히 일어나는 상황에선 벡터던 배열이던 비효율적이란 얘기~! 

 

장점 - [마지막 위치에 추가나 삭제는 쉬움]

위에 사진을 보면 바로 이해될거예요. 중간의 값을 빼거나 추가하는건 어렵지만, 맨 마지막에 더하거나 삭제하는건 쉽습니다. 하나씩 당겨주거나 하나씩 밀려야할 상황이 없잖아요?!

 

장점 - [구현이 용이]

차곡차곡 쌓는거니까 구현은 쉽습니다. 메모리에 순서대로 넣어주기만 하면 되잖아요? 머 편하게 사용하기 위한 여러 기능들좀 추가하고..

 

장점 - [랜덤적으로 직접접근 가능]

index를 이용해서 바로바로 값에 접근할 수 있다는 장점이 있어요. 내가 2000주소값에 A를 저장했다~ 만약 내가 찾고 싶은 C가 index2에 있는걸 알아, 그럼 알파벳은 1바이트니까 2000부터 0,1,2해서 3번째 2002번지 주소를 보면 된다는걸 알아요.

내가 찾으려는 데이터의 위치를 안다면 성능이 좋습니다.

#include <iostream>
int main()
{
    char array[7] = {'A','B','C','D','E','F','G'};
    std::cout<<array[2]; // index를 통해 C에 직접적으로 접근 가능
    return 0;
}

단점 - [다량의 데이터에서 검색이 느림]

위의 예시는 ABCDE..이렇게 이쁘게 순서대로 저장되어있지만 실제로 BZDA이런식으로 무작위의 데이터가 들어있을 수도 있죠. 내가 어떤 데이터를 찾아야해!, 그러면 결국 앞에서부터 뒤지는 수밖에 없습니다. 데이터가 100000000000개가 있는데 하필 내가 찾으려는 값이 뒤쪽에 있었다. 그럼 검색이 느리겠죠~~

3. 언제 벡터를 사용하는가

위 장점을 충족하고 단점을 피해갈 수 있을 때 사용하면 좋죠

 

내가 저장하려는 데이터 개수가 가변적일때,

중간에 데이터를 삽입하거나 삭제할 일이 없고 마지막에서 추가나 삭제정도만 있을 때 

저장할 데이터가 적거나, 많다면 검색이 빈번하지 않을 때,

랜덤하게 데이터 접근을 허용하게 하고 싶을때!

사용하면 되겠죠 간단명료!

4. 벡터를 사용하기 위한 헤더

벡터는 표준템플릿 라이브러리(STL)인데요, STL에서 제공되는 컨테이너는 일반적으로 사용하려고 하는 컨테이너 이름의 헤더파일명을 갖고 있습니다. 즉 라이브러리 이름이 vector예요.

#include <vector>

5. 벡터 다양한 선언 및 초기화

▶기본 선언문

벡터 컨테이너의 기본적인 선언 문법은 아래와 같습니다.

 

vector<자료형> [변수이름];

 

//빈 벡터를 생성한다.
vector<int> v1;
vector<char> v2;
vector<string> v3; 

배열도 int array[5]이런식으로 int 타입이 될 수도 있고 char array[5];와 같이 char타입으로 만들수 있듯이 vector도 꺽새 안에 만드려는 벡터의 자료형을 넣어줍시다.

↑빈 벡터를 생성하는 코드입니다.

 

생성자를 이용한 디폴트 선언 초기화

 

vector<자료형> [변수이름](개수)

 

이렇게 선언을 하시면 잡힌 공간이 디폴트 값으로 채워진채 vector가 생성됩니다.

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<char>v1(3);
	for (char x : v1) cout << x << ",";
	cout << endl;

	vector<int> v2(10);
	for (int x : v2) cout << x << ",";
	cout << endl;

	int n = 5;
	vector<int> v3(n);
	for (int x : v3) cout << x << ",";
	return 0;
}

결과

char의 경우 스페이스로, int일 경우 0으로 채워졌어요.

 

생성자를 이용한 선언 및 초기화

 

vector<자료형> [변수이름](개수, 값);

 

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v1(5, 2); 
	
	int n = 3; 
	vector<int> v2(n, 1); //요렇게 변수로 줘도 돼!
	
	//출력 
	cout<<"v1 elements:"; for(int x: v1) cout << x << " "; 
	cout << endl;
	cout<<"v2 elements:"; for(int x: v2) cout << x << " ";
	cout << endl;
	return 0;
}

결과

v1: index 0부터 index 4까지 값 2로 채워진 5개의 공간을 갖는 벡터, 

v2: 값 1로 채워진 n개의 공간을 갖는 벡터

 

▶'{}'를 사용한 선언과 초기화

 

vector<타입> [변수이름] {...채울 값...};

 

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<char> v1{ 'a','b','c' }; // C++11이상버전부터 
	for (char x : v1) cout << x << " ";
	cout << endl;

	vector<int> v2 = { 1,2,3,4 };
	for (int x : v2) cout << x << " ";
	return 0;
}

결과창

C++11부터 '{...}'를 이용해서 벡터를 초기화할 수 있습니다.

 

선언하고, push_back 함수 이용해 값 채우기 

push_back은 벡터 맨 뒤에 값을 밀어넣어주는 멤버함수입니다. 

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
	vector<string>v1;
	v1.push_back("a");
	v1.push_back("b");
	v1.push_back("c");
	for (string x : v1) cout << x << ",";
	cout << endl;

	vector<int>v2; int size = 10;
	for (int i = 0; i < size; i++)
	{
		v2.push_back(i+1);
	}
	for (int x : v2) cout << x << ",";
	return 0;
}

얘는 선언과 초기화를 한 번에 해주는게 아니라 각각 따로따로 선언하고 함수를 이용해서 값을 넣어 채우는 방법이죠!

이 외에 assign등 다양한 함수를 사용해서 값을 넣어줄수 있지만, 멤버함수에 관한건 2편에서 살펴봅시다.

6. 벡터 복사하기!

▶복사생성자 기본

 

vector<타입> [복사한걸저장하려는벡터명](복사하려는벡터명)

 

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1{ 1,2,3 };
    vector<int> v2(v1);
    for (int x: v2) cout<<x<<",";
    return 0;
}

v1이 1,2,3인데 v2에도 1,2,3이 잘 복사되어 저장된 것을 확인할 수 있어요. 가장 기본적인 복사생성자 타입~

 

대입 연산자 사용해서 복사 

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
	vector<char> v1 = { 'a','b','c' };
	vector<char> v2 = v1;
	for (char x : v2) cout << x << ",";
	return 0;
}

벡터는 연산자가 먹기 때문에 대입연산자를 이용해서 v2=v1 이런식으로 간단하게 복사 해줘도 됩니다.

 

▶iterator 사용해서 복사

#include<iostream>
#include<vector>
using namespace std;
int main() {
   vector<int> v1{ 1, 2, 3, 4, 5, 6 };
   vector<int> v2(v1.begin(), v1.end());
   vector<int> v3(v1.begin(), v1.begin()+3);
   //출력
   for (int x : v2) cout << x << ",";
   cout<<endl;
   for (int x : v3) cout << x << ",";
   return 0;
}

7. 가능한 연산자 대소비교

v1 == v2 v1과 v2의 원소가 같은가
v1 != v2 v1과 v2의 모든 원소 중 하나라도 다른 원소가 있는가
v1 = v2 v1에 v2를 복사 
v[i] v의 인덱스 i에 있는 원소를 참조한다.  
v1 > v2 v1이 v2보타 크다 
v1 >= v2 v1이 v2보다 크거나 같다
v1 < v2 v1이 v2보다 작다
v1 <= v2  v1이 v2보다 작거타 같다. 

[<,<=,>,>=]

#include<iostream>
#include<vector>
using namespace std;
int main() {
    vector<int> v1{ 1, 2, 3, 4, 5, 6 };
    vector<int> v2{ 2, 3, 4 };
    if (v1 > v2)
        printf("v1>v2");
    else
        printf("v2>v1");
    return 0;
}

<,<=,>,>= 의 경우 특정 index를 지정해주지 않으면 벡터의 첫 번째 값을 비교합니다.

 

오늘은 여기까지입니다. 고생하셨습니다~

도움이 되셨다면 공감/덧글/광고보답은 어떤가요? 여러분들과 정보공유하는데 큰 힘이 됩니다 :)

  • 나그네 2020.10.08 21:09

    와아 정말 감사합니다
    생성자를 이용해서 생성라면 heap영역에 생성되서 나중에 free를 해야하는건가요?

    • IT 양햄찌(jhnyang) 2020.10.12 10:27 신고

      벡터는 자동으로 메모리 관리하여 해제해주기 때문에 별도로 free를 해주지 않아도 됩니다. 그래서 동적할당을 하는 것보다 동적 메모리를 관리하는 STL을 사용하는 것이 권장되죠 :)