안녕하세요, 여러분! 오늘은 C++ STL의 꽃이라고 할 수 있는 vector
에 대해 함께 알아보는 시간을 가져보려고 해요. 마치 만능 주머니처럼 자유자재로 요소를 추가하고 삭제할 수 있는 vector
는 정말 매력적이지 않나요? 프로그래밍을 하다 보면 데이터를 효율적으로 관리해야 할 때가 정말 많은데, 이럴 때 vector
만큼 든든한 친구도 없답니다. 요소 추가
, 삭제
, 접근
등 vector
의 다양한 사용법을 제대로 익히면 코딩 실력이 쑥쑥 향상될 거예요. 자, 그럼 지금부터 벡터 생성 및 초기화
부터 요소 접근
까지 차근차근 살펴보도록 할까요? 함께 즐겁게 vector
의 세계로 빠져들어 봐요!
벡터 생성 및 초기화
C++ STL의 vector
는 동적 배열처럼 작동하는 컨테이너예요. 크기가 자동으로 조정되니 얼마나 편한지 몰라요! 자, 그럼 이 마법 상자를 어떻게 만드는지, 또 어떤 마법의 주문을 걸 수 있는지 하나씩 알아볼까요?
헤더파일 포함
먼저, vector
를 사용하려면 <vector>
헤더 파일을 포함해야 해요. 잊지 마세요!
#include <vector>
벡터 생성
자, 이제 vector
를 만들어 볼게요. vector
는 다양한 자료형을 저장할 수 있어요. 정수, 실수, 문자열, 심지어 사용자 정의 클래스까지도 가능해요! vector
를 생성하는 방법은 여러 가지가 있어요. 가장 기본적인 방법은 다음과 같아요.
std::vector<int> vec; // int형 벡터 생성, 요소 없음
이렇게 하면 비어있는 int
형 벡터 vec
가 생성돼요. 초기값을 넣어서 생성하고 싶다면 어떻게 해야 할까요? 여러 가지 방법이 있지만, 몇 가지 예시를 보여드릴게요.
std::vector<int> vec1 = {1, 2, 3, 4, 5}; // 초기값을 직접 넣어 생성!
std::vector<double> vec2(3, 3.14); // 크기가 3이고 모든 요소가 3.14인 double형 벡터 생성!
std::vector<std::string> vec3(5, "hello"); // 크기가 5이고 모든 요소가 "hello"인 string형 벡터 생성!
std::vector<int> vec4(vec1); // vec1을 복사해서 vec4 생성!
std::vector<int> vec5(vec1.begin(), vec1.begin() + 3); // vec1의 처음 세 요소를 복사해서 vec5 생성!
벡터 생성 방법
각각의 방법을 좀 더 자세히 살펴볼까요? 첫 번째 방법은 초기값 리스트를 사용해서 벡터를 생성하는 방법이에요. {}
괄호 안에 초기값들을 넣어주면 돼요. 간단하죠? 두 번째 방법은 벡터의 크기와 초기값을 지정해서 생성하는 방법이에요. vec2
처럼 크기가 3이고 모든 요소가 3.14인 double
형 벡터를 만들 수 있어요. 세 번째 방법도 비슷해요. 크기와 초기값을 지정해서 문자열 벡터를 만들 수 있답니다. 네 번째 방법은 다른 벡터를 복사해서 새로운 벡터를 생성하는 방법이에요. vec1
의 모든 요소가 vec4
에 복사돼요. 마지막 방법은 iterator를 사용해서 벡터의 일부분을 복사하는 방법이에요. vec1
의 처음 세 요소만 vec5
에 복사되는 거죠!
벡터 크기 변경
vector
의 크기를 변경하고 싶으면 어떻게 해야 할까요? resize()
함수를 사용하면 돼요!
std::vector<int> vec = {1, 2, 3};
vec.resize(5); // 크기를 5로 변경! 부족한 부분은 0으로 채워져요!
std::cout << vec.size(); // 출력: 5
vec
의 크기가 5로 변경되었고, 부족한 두 자리는 0으로 채워졌어요. 만약 특정 값으로 채우고 싶다면 두 번째 인자로 값을 넣어주면 돼요.
vec.resize(8, 99); // 크기를 8로 변경하고, 99로 채워요!
이제 vec
의 크기는 8이고, 새로 추가된 다섯 자리는 99로 채워져요! vector
는 크기가 동적으로 변경되기 때문에 메모리 관리 측면에서도 효율적이에요. 필요한 만큼만 메모리를 사용하니까요! vector
는 C++에서 가장 많이 사용되는 컨테이너 중 하나예요. 다양한 기능을 제공하고 사용하기도 편리하기 때문이죠!
요소 추가 방법
자, 이제 벡터에 요소를 추가하는 다양한 방법을 알아볼까요? 마치 레고 블록을 쌓듯이, 원하는 요소들을 차곡차곡 넣어 벡터를 풍성하게 만들 수 있답니다! C++ STL의 vector
는 동적 배열로, 크기가 자동으로 조정되기 때문에 요소 추가가 매우 편리해요. 마법 같죠? ^^
push_back() 함수를 사용하는 방법
가장 기본적인 방법은 push_back()
함수를 사용하는 거예요. 이 함수는 벡터의 맨 뒤에 새로운 요소를 추가한답니다. 예를 들어, 숫자 1부터 5까지를 벡터에 추가하고 싶다면 다음과 같이 작성할 수 있어요. 참 쉽죠?!
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector;
myVector.push_back(1);
myVector.push_back(2);
myVector.push_back(3);
myVector.push_back(4);
myVector.push_back(5);
return 0;
}
push_back()
은 벡터의 크기를 자동으로 늘려주기 때문에, 용량 걱정 없이 요소를 추가할 수 있다는 장점이 있어요! 벡터의 크기를 미리 알고 있다면 reserve()
함수를 사용하여 메모리 할당 횟수를 줄이고 성능을 향상시킬 수도 있답니다. reserve()
는 벡터의 용량을 예약하는 함수인데, 예를 들어 처음부터 100개의 요소를 추가할 예정이라면 myVector.reserve(100);
과 같이 사용할 수 있어요. 이렇게 하면 메모리 재할당 횟수가 줄어들어 효율적이겠죠?
insert() 함수를 사용하는 방법
push_back()
외에도 insert()
함수를 사용하면 벡터의 특정 위치에 요소를 삽입할 수 있어요. insert()
함수는 iterator를 사용하는데, iterator는 벡터의 특정 요소를 가리키는 포인터와 같은 역할을 해요. 예를 들어, 벡터의 두 번째 위치에 숫자 10을 삽입하고 싶다면 다음과 같이 작성할 수 있답니다.
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector;
myVector.push_back(1);
myVector.push_back(2);
// 벡터의 begin() + 1 위치, 즉 두 번째 위치에 10을 삽입합니다.
myVector.insert(myVector.begin() + 1, 10);
return 0;
}
myVector.begin()
은 벡터의 첫 번째 요소를 가리키는 iterator를 반환하고, 여기에 1을 더하면 두 번째 요소를 가리키게 되는 거예요. insert()
함수는 지정된 위치에 새로운 요소를 삽입하고, 기존 요소들은 뒤로 한 칸씩 이동한답니다. 마치 손님이 꽉 찬 버스에 새로운 승객이 타면 자리를 만들어주는 것과 같은 원리예요! insert()
함수를 사용할 때 주의할 점은, 삽입 위치가 벡터의 크기를 넘어가면 안 된다는 거예요. 만약 범위를 벗어난 위치에 삽입하려고 하면 프로그램이 오류를 발생시킬 수 있으니 조심해야 해요!
insert() 함수를 사용하여 여러 개의 요소를 한 번에 추가하는 방법
또한, insert()
함수를 사용하여 여러 개의 요소를 한 번에 추가할 수도 있어요. 예를 들어, 벡터의 세 번째 위치에 숫자 20부터 24까지 5개의 요소를 추가하고 싶다면 다음과 같이 작성할 수 있답니다.
#include <iostream>
#include <vector>
#include <numeric>
int main() {
std::vector<int> myVector = {1, 2, 3};
// 20부터 24까지의 숫자를 생성하여 tempVector에 저장합니다.
std::vector<int> tempVector(5);
std::iota(tempVector.begin(), tempVector.end(), 20);
// myVector의 세 번째 위치(begin() + 2)에 tempVector의 모든 요소를 삽입합니다.
myVector.insert(myVector.begin() + 2, tempVector.begin(), tempVector.end());
return 0;
}
insert()
함수의 세 번째와 네 번째 인자로 삽입할 요소의 시작과 끝 iterator를 지정해주면 된답니다. insert()
함수를 사용할 때는 iterator의 개념을 잘 이해하는 것이 중요해요! iterator는 벡터의 요소를 가리키는 포인터와 같은 역할을 하기 때문에, iterator를 잘못 사용하면 예상치 못한 결과가 발생할 수 있으니 주의해야 한답니다!
emplace_back() 함수를 사용하는 방법
emplace_back()
함수는 C++11에서 추가된 기능인데, push_back()
과 비슷하지만 더 효율적인 방법으로 요소를 추가할 수 있어요. push_back()
은 먼저 요소를 생성한 다음 벡터에 복사하지만, emplace_back()
은 벡터 내부에서 직접 요소를 생성하기 때문에 복사 과정이 생략되어 성능이 향상될 수 있답니다. 특히 복잡한 객체를 추가할 때 emplace_back()
을 사용하면 성능 차이가 더욱 커질 수 있어요. emplace_back()
은 객체를 생성하는 데 필요한 인자들을 직접 전달받아 벡터 내부에서 객체를 생성한답니다. 신기하죠?
이처럼 C++ STL의 vector
는 다양한 방법으로 요소를 추가할 수 있도록 지원하고 있어요. 각 함수의 특징과 사용법을 잘 이해하고 상황에 맞는 함수를 사용하면 더욱 효율적이고 안전하게 벡터를 사용할 수 있답니다! 다음에는 벡터에서 요소를 삭제하는 방법에 대해 알아볼게요! 기대해 주세요~?
요소 삭제 방법
자, 이제 벡터에서 원소를 삭제하는 방법에 대해 알아볼까요? 생각보다 다양한 방법이 있고, 각각의 특징이 있으니 잘 따라와 주세요! 벡터의 크기가 동적으로 변한다는 점, 잊지 않으셨죠? 삭제 작업 후에는 벡터의 크기가 줄어들고, 메모리 관리도 자동으로 이루어진답니다!
C++ STL의 <vector>
는 요소 삭제를 위해 pop_back()
, erase()
, clear()
등 다양한 멤버 함수를 제공해요. 각 함수의 시간 복잡도와 사용법을 제대로 이해하는 것이 성능 최적화의 핵심이라고 할 수 있죠! 벡터에서 요소를 삭제할 때, 잘못된 방법을 사용하면 프로그램이 예상치 못한 동작을 하거나 심지어 크래시가 발생할 수도 있어요. 그러니 각 함수의 특징을 제대로 파악하고 상황에 맞는 함수를 사용하는 것이 정말 중요해요!
1. pop_back()
벡터의 맨 마지막 요소를 삭제하는 가장 간단한 방법이에요. 시간 복잡도는 상수 시간, 즉 O(1)이라서 매우 빠르죠. 벡터가 비어있는 상태에서 pop_back()
을 호출하면 정의되지 않은 동작이 발생할 수 있으니, 사용 전에 empty()
로 벡터가 비어있는지 확인하는 습관을 들이는 게 좋겠죠? 예를 들어, 1000개의 요소가 있는 벡터에서 마지막 요소를 삭제하는 데에는 1ms도 걸리지 않을 거예요. 하지만, 맨 마지막 요소가 아닌 다른 요소를 삭제하려면 erase()
를 사용해야 한다는 점! 꼭 기억해 두세요!
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
myVector.pop_back(); // 5가 삭제됩니다!
// 이제 myVector는 {1, 2, 3, 4}가 됩니다.
return 0;
}
2. erase()
특정 위치 또는 범위의 요소를 삭제할 때 사용하는 함수입니다. pop_back()
보다 훨씬 유연하지만, 시간 복잡도는 삭제되는 요소의 개수와 위치에 따라 선형 시간, 즉 O(n)까지 증가할 수 있어요. (n은 벡터의 크기) 특히 벡터의 앞부분에서 요소를 삭제하면, 뒤에 있는 모든 요소들이 한 칸씩 앞으로 이동해야 하기 때문에 시간이 오래 걸릴 수 있다는 점, 주의해야 해요!
단일 요소 삭제
이터레이터를 사용하여 특정 위치의 요소를 삭제할 수 있어요.
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
myVector.erase(myVector.begin() + 2); // 인덱스 2의 요소 (3) 삭제
// 이제 myVector는 {1, 2, 4, 5}가 됩니다.
return 0;
}
범위 삭제
시작과 끝 이터레이터를 지정하여 특정 범위의 요소를 한 번에 삭제할 수도 있어요. 범위는 시작 이터레이터부터 끝 이터레이터 바로 전까지라는 점, 잊지 마세요!
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
myVector.erase(myVector.begin() + 1, myVector.begin() + 4); // 인덱스 1부터 3까지 (2, 3, 4) 삭제
// myVector는 {1, 5}가 됩니다.
return 0;
}
3. clear()
벡터의 모든 요소를 삭제하고 벡터를 비웁니다. 시간 복잡도는 벡터의 크기에 비례하는 선형 시간, O(n)입니다. clear()
를 사용하면 벡터의 크기가 0이 되지만, 할당된 메모리는 그대로 유지된다는 점! (용량은 변하지 않아요) 메모리까지 완전히 해제하려면 clear()
후에 shrink_to_fit()
을 호출하거나, swap trick (std::vector<T>().swap(myVector)
)을 사용하는 것이 좋습니다.
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
myVector.clear(); // 모든 요소 삭제!
// myVector는 이제 비어있습니다.
return 0;
}
자, 이제 벡터에서 요소를 삭제하는 다양한 방법들을 알아보았어요! 각 함수의 시간 복잡도와 특징을 잘 이해하고 상황에 맞게 사용하면, 효율적이고 안전하게 벡터를 관리할 수 있을 거예요. 다음에는 벡터 요소에 접근하는 방법에 대해 알아보도록 할게요! 기대해 주세요!
벡터 요소 접근
자, 이제 벡터에 원하는 데이터들을 예쁘게 담았으니 이 데이터들을 어떻게 꺼내 쓸 수 있는지 알아볼 차례예요! 마치 보물상자에서 원하는 보물을 꺼내듯이 말이죠! C++ STL 벡터는 다양한 방법으로 요소에 접근할 수 있도록 여러 가지 기능들을 제공한답니다. 각각의 방법들을 하나씩 살펴보면서 여러분의 코딩 실력을 한 단계 업그레이드해 보아요~!
대괄호 연산자 및 at() 메서드
가장 기본적이고, 직관적인 방법은 []
연산자, 즉 대괄호 연산자를 사용하는 거예요. 마치 배열처럼 말이죠! 예를 들어 vector<int> myVector = {10, 20, 30, 40, 50};
이렇게 벡터를 선언하고, 세 번째 요소(30)에 접근하고 싶다면 myVector[2]
라고 하면 된답니다. 인덱스는 0부터 시작한다는 점, 잊지 마세요?! 만약 myVector[5]
처럼 존재하지 않는 인덱스에 접근하려고 하면 어떻게 될까요? 프로그램이 크래시 날 수도 있어요! (런타임 에러!) 이런 상황을 방지하려면 at()
메서드를 사용하는 것이 훨씬 안전해요.
at()
메서드는 []
연산자와 기능은 비슷하지만, 범위를 벗어난 인덱스에 접근하면 std::out_of_range
예외를 던져준답니다. 훨씬 안전하죠? 예외 처리를 통해 프로그램의 안정성을 높일 수 있다는 장점이 있어요! 예를 들어 myVector.at(2)
는 세 번째 요소(30)를 안전하게 반환해주지만, myVector.at(10)
과 같이 범위를 벗어난 인덱스를 사용하면 예외가 발생하고, 이를 try-catch
문으로 잡아서 적절히 처리할 수 있답니다. 프로그램이 갑자기 죽는 것보다는 훨씬 낫겠죠?
front() 메서드와 back() 메서드
벡터의 첫 번째 요소와 마지막 요소에 접근하는 특별한 방법도 있어요. front()
메서드는 벡터의 첫 번째 요소에 대한 참조를 반환하고, back()
메서드는 벡터의 마지막 요소에 대한 참조를 반환해요. myVector.front()
는 10을, myVector.back()
은 50을 반환하겠죠? 이 메서드들을 사용하면 벡터의 시작과 끝 요소에 쉽고 빠르게 접근할 수 있어요. 특히 큐나 스택처럼 자료구조를 구현할 때 아주 유용하게 사용할 수 있답니다!
Iterator(반복자)를 사용한 접근
자, 이제 벡터의 요소들을 하나씩 순차적으로 접근하는 방법을 알아볼까요? 반복문을 사용하는 것이 일반적이지만, C++에서는 더욱 효율적이고 세련된 방법을 제공해요. 바로 iterator(반복자)를 사용하는 것이죠! 마치 벡터를 가리키는 포인터처럼 생각하면 돼요. begin()
메서드는 벡터의 첫 번째 요소를 가리키는 iterator를 반환하고, end()
메서드는 벡터의 마지막 요소 다음을 가리키는 iterator를 반환해요. end()
가 마지막 요소를 가리키는 게 아니라, 마지막 요소 다음을 가리킨다는 점! 꼭 기억하세요!
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector = {10, 20, 30, 40, 50};
for (std::vector<int>::iterator it = myVector.begin(); it != myVector.end(); ++it) {
std::cout << *it << " "; // iterator가 가리키는 값에 접근하려면 * 연산자를 사용해야 해요!
}
std::cout << std::endl;
// C++11 이후부터는 range-based for loop를 사용하여 더 간결하게 표현할 수 있어요!
for (int value : myVector) {
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}
위 코드처럼 iterator를 사용하면 벡터의 모든 요소를 순차적으로 접근할 수 있답니다. C++11 이후부터는 range-based for loop를 사용하여 더 간결하게 표현할 수도 있어요! 훨씬 깔끔하죠? 코드가 짧아지면 오류 발생 가능성도 줄어들고, 가독성도 좋아진답니다.
rbegin()과 rend() 메서드
rbegin()
과 rend()
메서드는 역방향 iterator를 반환해요. rbegin()
은 벡터의 마지막 요소를 가리키고, rend()
는 벡터의 첫 번째 요소 앞을 가리킨답니다. 이 메서드들을 사용하면 벡터의 요소들을 역순으로 접근할 수 있어요. 가끔씩 필요한 기능이니 알아두면 좋겠죠?
벡터 요소에 접근하는 방법, 이제 완벽하게 이해하셨나요? 다양한 방법들을 적재적소에 활용하여 여러분의 C++ 코딩 실력을 한층 더 업그레이드해보세요!
자, 이렇게 C++ STL vector에 대해 알아봤어요! 생성부터 초기화, 요소 추가, 삭제, 그리고 접근까지, 어떤가요? 이제 좀 친숙해진 느낌이 드나요? 처음엔 조금 낯설 수 있지만, 몇 번 연습하다 보면 금방 손에 익을 거예요. 벡터는 정말 강력하고 유용한 도구니까요. 마치 요술 상자처럼 다양하게 활용할 수 있답니다! 앞으로 C++ 프로그래밍하면서 벡터와 더욱 친해져 보세요. 분명 코딩하는 재미가 더 쏠쏠해질 거예요. 이 글이 여러분의 C++ 여정에 조금이나마 도움이 되었으면 좋겠네요. 다음에 또 만나요!