c++에서 제공하는 표준 템플릿 라이브러리 STL에는 vector, stack, queue, pair, map 등의 컨테이너가 있다. 컨테이너의 종류와 각 멤버함수들을 알아보자.
- 컨테이너와 iterator란?
- vector 컨테이너
- stack 컨테이너
- queue 컨테이너
- pair 컨테이너
- Map 컨테이너
- Algorithm STL
- 범위 기반 for문
- 람다식
컨테이너는 같은 타입의 데이터, 객체를 저장하고 관리하는 역할을 한다.
iterator(반복자)는 컨테이너 원소의 주소를 저장하는 포인터이다.
(컨테이너 이름)<자료형>::iterator (반복자 이름);
크기가 자유로운 배열을 구현한 컨테이너이다.
pop_back() front() back() begin() end() size() empty() insert(it, data) erase(it)
#include <iostream>
#include <vector> //헤더파일
using namespace std;
int main(){
vector<int> v; //벡터 생성
for(int i=0;i<5;i++) v.push_back(i+1); // push_back() 마지막에 data 추가
cout<<"값 변경 전: ";
cout<<v.front()<<' '; //front() 맨 앞 원소 반환
cout<<v.back()<<'\n'; //back() 맨 뒤 원소 반환
vector<int>::iterator it; //컨테이너 원소를 가리키는 포인터 iterator 선언
it = v.begin(); //begin() 맨 앞 원소의 주소를 반환
//iterator가 첫 번째 원소를 가리키도록
while(it!=v.end()){ //end() 맨 뒤 원소의 주소 + 1의 주소를 반환
*it+=10; // **it : iterator가 가리키는 값을 10 증가시킴
it++; // it 주소 한 칸 증가
}
cout<<"값 변경 후: ";
cout<<v.front()<<' ';
cout<<v.back()<<'\n';
}
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(){
vector<string> memStack; //후입선출 멤버 list
vector<string>::iterator it;
while(1){
cout<<"=================Member=================\n";
cout<<"추가(1) 삭제(2) 멤버수 출력(3) 종료(4)\n";
int f; cin>>f;
if(f==1){
cout<<"추가할 멤버의 이름 : ";
string stmp; cin>>stmp;
it = memStack.begin();
memStack.insert(it, stmp); //insert(it, data) it가 가리키는 위치에 data 추가
}
else if (f==2){
cout<<"삭제할 멤버의 이름 : ";
string targetS; cin>>targetS;
it = memStack.begin();
while(it!=memStack.end()){
if(targetS == *it) break;
}
memStack.erase(it); //erase(it) it가 가리키는 위치의 원소 삭제
}
else if(f==3){
if(memStack.empty()){
cout<<"리스트에 아무도 없습니다.\n"; //empty() 벡터가 비어있으면 true 반환
}
else cout<< memStack.size()<<'\n'; //size() 벡터의 사이즈 반환
}
else{
break;
}
}
cout<<"프로그램이 종료되었습니다.";
return 0;
}
LIFO 형식의 크기가 자유로운 배열이다.
pop() top() empty() size()
2) 멤버함수
#include <iostream>
#include <stack>
using namespace std;
int main(){
stack<string> bookStack; //바닥에 쌓아올리는 나의 책더미들~
while(1){
cout<<"==================Book==================\n";
cout<<"쌓기(1) 내리기(2) 책 개수 출력(3) 종료(4)\n";
int f; cin>>f;
if(f==1){
cout<<"올려둘 책 제목 : ";
string stmp; cin>>stmp;
bookStack.push(stmp); //push(data) 맨 뒤에 data 추가
}
else if(f==2){
bookStack.pop(); //pop() 스택의 마지막 원소 제거
}
else if(f==3){
if(bookStack.empty()){ //empty() 스택이 비어있으면 true
cout<<"바닥에 책이 없는데용?\n";
}
else cout<<bookStack.size(); //size() 스택 사이즈 반환
}
else if(f==4){
break;
}
}
cout<<"엄마에게 혼나서 더 이상 바닥에 책을 둘 수 없어.. 책꽂이에 꽂도록 하자..\n";
return 0;
}
1) stack vs queue
FIFO 형식의 크기가 자유로운 배열
pop() top() empty() size()
stack은 LIFO 형식이므로 pop()이 가장 마지막 원소를 제거하는 반면, queue에서는 맨 앞 원소를 제거한다. 반면, push()는 stack과 queue 라이브러리 모두에서 맨 뒤에 원소를 추가한다.
두 개체(자료형이 다를 수도 있다)를 단일 개체로 묶어 관리할 수 있게 해주는 컨테이너이다.
pair<자료형1, 자료형2> 이름(사이즈);
pair<int, string> p;
p.first = 3;
p.second = "hi";
#include <utility>
참고로 vector 헤더파일은 utility 헤더를 포함하고 있기 때문에 vector와 pair 컨테이너를 함께 쓰고 싶다면 vector 헤더파일만 선언해주면 된다.
#include <iostream>
#include <utility> //헤더 파일
#include <string>
using namespace std;
int main(){
pair<string, int> myInfo; //pair 컨테이너 선언
myInfo.first = "한정화"; //.first 첫 번째 원소 (여기에선 string 개체)
myInfo.second = 23 ; //.second 두 번째 원소 (여기에선 int 개체)
cout<<"내 이름은 "<<myInfo.first<<"이고, 나이는 "<<myInfo.second<<"입니다.";
}
#include <iostream>
#include <vector> //utility 헤더 포함
#include <string>
using namespace std;
int main(){
vector<pair<string, int>> wishTem(3);
for(int i=0;i<3;i++){
string stmp;
int ptmp;
cout<<"위시템 상품명 : ";
cin>>stmp;
cout<<"위시템 가격 : ";
cin>>ptmp;
wishTem[i].first = stmp; //벡터의 i번째 pair의 값 설정
wishTem[i].second = ptmp;
}
for(int i=0;i<3;i++){
cout<<"위시템 "<<wishTem[i].first<<", "<<wishTem[i].second<<"원\n";
}
}
key와 value를 묶어 관리하는 컨테이너이다.
map<key자료형, value자료형> 이름(사이즈);
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main(){
map<int, string> student;
student.insert({30,"junghwa"}); //insert({key, value}) map 변수 생성방법2
student[20] = "soyeon"; //이름[key] = value map 변수 생성방법1
cout<<student.size()<<'\n'; //size() 사이즈 반환
cout<<"id가 30인 학생은"<<student[30]<<"입니다.";
}
begin() end() size() empty() find(key) erase(it) operator[key]()
#include <iostream>
#include <map>
using namespace std;
int main(){
map<string, string> EngKodict;
cout<<"=======dictionary======\n";
while(1){
string word, meaning;
cout<<"영단어와 뜻 입력>> ";
cin>>word>>meaning;
if(word=="end" && meaning=="end") break;
EngKodict.insert({word, meaning});
}
cout<<"===English to korean===\n";
while(1){
string word;
cout<<"영단어 : ";
cin>>word;
if(word=="end") break;
cout<<word<<"의 뜻 : "<<EngKodict[word]<<'\n';
}
}
algorithm STL 은 다양한 컨테이너와 iterator를 이용해 다양한 기능을 제공한다.
quick sort로 구현되어있어 속도가 매우 빠르다.
cpp sort(정렬 시작 주소, 정렬 끝 주소+1, 비교함수)
#include <iostream>
#include <algorithm>
using namespace std;
int arr[5];
int main(){
cout<<"배열 데이터 입력: ";
for(int i=0;i<5;i++){
cin>>arr[i];
}
sort(arr, arr+5); //sort(정렬 시작 주소, 정렬 끝 주소+1)
//c++에서 배열은 첫 번째 원소를 가리키는 주소이므로 이렇게 쓸 수 있다.
cout<<"오름차순 정렬: ";
for(int i=0;i<5;i++) cout<<arr[i]<<' ';
}
#include <iostream>
#include <algorithm>
using namespace std;
int arr[5];
bool cmp(int a, int b){ //비교함수
if(a > b) return true; //자리 변경 o
else return false; //자리 변경 x
}
int main(){
cout<<"배열 데이터 입력: ";
for(int i=0;i<5;i++){
cin>>arr[i];
}
sort(arr, arr+5, cmp); //sort(정렬 시작 주소, 정렬 끝 주소+1, 비교함수)
cout<<"내림차순 정렬: ";
for(int i=0;i<5;i++) cout<<arr[i]<<' ';
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
cout<<"배열 사이즈 입력: ";
int n;
cin>>n;
cout<<"배열 데이터 입력: ";
vector<string> v(n);
for(int i=0;i<n;i++){
cin>>v[i];
}
sort(v.begin(), v.end()); //sort(정렬 시작 주소, 정렬 끝 주소+1)
cout<<"사전순 정렬: ";
for(int i=0;i<n;i++) cout<<v[i]<<' ';
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool cmp(string s1, string s2){
if(s1 < s2) return true;
else return false;
}
int main(){
int n;
cout<<"배열 사이즈 입력: ";
cin>>n;
vector <string> v(n);
cout<<"배열 데이터 입력: ";
for(int i=0;i<n;i++){
cin>>v[i];
}
sort(v.begin(), v.end());
cout<<"사전역순 정렬: ";
for(int i=0;i<n;i++) cout<<v[i]<<' ';
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct person {
int age;
string name;
};
bool cmp(person p1, person p2) {
if (p1.age == p2.age) {
return p1.name < p2.name;
}
return p1.age < p2.age;
}
int main() {
int n;
cout << "배열 사이즈 입력: ";
cin >> n;
vector<person> v(n);
cout << "-" << n << "명의 데이터 입력-\n";
for (int i = 0; i < n; i++) cin >> v[i].age >> v[i].name;
sort(v.begin(), v.end(), cmp);
cout << "-나이순 정렬-\n";
for (int i = 0; i < n; i++) cout << v[i].age << ' ' << v[i].name << '\n';
}
for_each(시작 주소, 끝 주소+1, 반복실행함수)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void print(int n) {
cout << n << ' ';
}
int main() {
vector <int> v = { 1,2,3,4,5 };
for_each(v.begin(), v.end(), print); // for_each(시작주소, 끝주소+1, 반복실행함수)
}
위 코드에서 for_each 함수의 작동 원리는 다음과 동일하다 :
vector <int>::iterator it;
for (it= v.begin(); it < v.end(); it++) {
print(*it);
}
c++11에 새로 추가됐다고 한다(오예)
for(자료형 변수명 : 컨테이너 이름)
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector <int> v = { 1,2,3,4,5 };
for (int k : v) { // for(자료형 변수명 : 컨테이너 이름)
cout << k << ' ';
}
}
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Person {
public:
Person(string name, int age) : name(name), age(age) {}
void newYear() { age++; }
void getInfo() { cout << name << "은 " << age << "살이다. \n"; }
private:
int age;
string name;
};
vector <Person> v;
//vector <Person> v; ->빈 컨테이너라서 오류 안 남
//vector <Person> v(3); -> 3개를 생성해두는 것이므로 기본 생성자가 없으면 에러가 남
int main() {
Person junghwa("junghwa", 23), soyeon("soyeon", 23), heesu("heesu", 23);
v.push_back(junghwa);
v.push_back(soyeon);
v.push_back(heesu);
for (Person p : v) { // for(자료형 변수명 : 컨테이너 이름)
p.getInfo();
}
cout << "Happy New Year!\n";
for (Person p : v) { // for(자료형 변수명 : 컨테이너 이름)
p.newYear();
p.getInfo();
}
}
auto는 컴파일러가 자료형을 추측하도록 지시하는 명령어이다. 이 또한 c++11에 추가된 기능이다.
#include <iostream>
#include <string>
using namespace std;
int main() {
auto p1 = 3.14;
auto n = 3;
auto str = "파이 소숫점 이하 20자리까지 외울 수 있는 사람?";
cout << p1 << '\n' << n << '\n' << str;
}
배열은 불가능하다.
클래스, 구조체도 가능하다.
위의 (1)범위 기반 for문 예제 2에서의
for (Person p : v) { // for(자료형 변수명 : 컨테이너 이름)
p.getInfo();
}
이 코드를
for(auto c: v){
c.getInfo();
}
로도 쓸 수 있다.
위와 같이 범위 기반 for문과 함께 쓸 때 auto가 유용하게 쓰인다.