-다른 객체(원소)들을 보관하는 하나의 커다란 보관소. STL 컨테이너는 클래스 템플릿(class template)의 형태로 구현되어 있기 때문에 임의의 타입의 원소들을 위한 컨테이너를 만들 수 있다. 물론 한 컨테이너에는 한가지 종류의 객체들만 보관할 수 있다.
-자신이 보관하는 원소들의 메모리를 관리하며, 각각의 원소에 접근할 수 있도록 멤버 함수를 제공한다. 컨테이너 상에서 원소에 접근하는 방법은 두가지가 있다. 하나는 직접 함수를 호출해서 접근, 다른 하나는 반복자(iterator)을 이용해서 접근하는 것
// classes_as_friends1.cpp
// compile with: /c
class B;
class A {
public:
int Func1( B& b );
private:
int Func2( B& b );
};
class B {
private:
int _b;
// A::Func1 is a friend function to class B
// so A::Func1 has access to all members of B
friend int A::Func1( B& );
};
int A::Func1( B& b ) { return b._b; } // OK
int A::Func2( B& b ) { return b._b; } // C2248
위의 예제에서는 A::Func1( B& ) 함수에만 B 클래스에 대한 friend 액세스 권한이 부여됩니다. 따라서 private 멤버에 대한 _b 액세스는 클래스의 에서 Func1 올바르지만 A 에서는 올바르지 Func2 않습니다.
friend class A;
friend 클래스는 모든 멤버 함수가 클래스의 friend 함수인 클래스입니다. 즉, 멤버 함수가 다른 클래스의 전용 및 보호된 멤버에 액세스할 수 있습니다.
이 경우 A 클래스의 모든 멤버 함수에 B 클래스에 대한 freind 액세스 권한이 부여되었습니다.
for (typename std::vector<T>::iterator itr = vec.begin(); itr != vec.end();
++itr) {
와 같이 typename을 추가해줘야함. iterator 가 std::vector 의 의존 타입이기 때문.
// 반복자 사용 예시
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
vec.push_back(40);
// 전체 벡터를 출력하기
for (std::vector<int>::iterator itr = vec.begin(); itr != vec.end(); ++itr) {
std::cout << *itr << std::endl;
}
// int arr[4] = {10, 20, 30, 40}
// *(arr + 2) == arr[2] == 30;
// *(itr + 2) == vec[2] == 30;
std::vector<int>::iterator itr = vec.begin() + 2;
std::cout << "3 번째 원소 :: " << *itr << std::endl;
}
이터레이터를 포인터 처럼 사용한다고 하였다, 실제로 현재 이터레이터가 가리키는 원소의 값을 보고 싶다면
std::cout << *itr << std::endl;
포인터로 를 사용해서 가르키는 주소값의 값을 보았던 것 처럼, 를 이용해서 itr이 가르키는 원소를 볼 수 있다.
물론 itr는 실제 포인터가 아니고 *연산자를 오버로딩해서 마치 포인터 처럼 동작하게 만든 것 이다. *연산자는 itr이 가르키는 원소의 레퍼런스를 리턴한다.
또한, + 연산자를 통해 그 만큼 떨어져 있는 원소를 가리키게 할 수도 있다.
#include <iostream>
#include <vector>
template <typename T>
void print_vector(std::vector<T>& vec) {
// 전체 벡터를 출력하기
for (typename std::vector<T>::iterator itr = vec.begin(); itr != vec.end();
++itr) {
std::cout << *itr << std::endl;
}
}
int main() {
std::vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
vec.push_back(40);
std::cout << "처음 벡터 상태" << std::endl;
print_vector(vec);
std::cout << "----------------------------" << std::endl;
// vec[2] 앞에 15 추가
vec.insert(vec.begin() + 2, 15);
print_vector(vec);
std::cout << "----------------------------" << std::endl;
// vec[3] 제거
vec.erase(vec.begin() + 3);
print_vector(vec);
}
#include <iostream>
#include <vector>
template <typename T>
void print_vector(std::vector<T>& vec) {
// 전체 벡터를 출력하기
std::cout << "[ ";
for (typename std::vector<T>::iterator itr = vec.begin(); itr != vec.end();
++itr) {
std::cout << *itr << " ";
}
std::cout << "]";
}
int main() {
std::vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
vec.push_back(40);
vec.push_back(20);
std::cout << "처음 벡터 상태" << std::endl;
print_vector(vec);
std::vector<int>::iterator itr = vec.begin();
std::vector<int>::iterator end_itr = vec.end();
for (; itr != end_itr; ++itr) {
if (*itr == 20) {
vec.erase(itr);
}
}
std::cout << "값이 20 인 원소를 지운다!" << std::endl;
print_vector(vec);
}
위와 같은 상황에선 오류가 발생한다.
이유 :: 컨테이너에 원소를 추가하거나 제거할 시 기존에 사용하였던 모든 이터레이터들을 사용할 수 없게 된다. 위의 경우 vec.erase(itr)을 수행하게 되면 더 이상 itr은 유효한 반복자가 아니게 되는 것. 또한, end_itr 역시 무효화 된다. 따라서, itr != end_itr 이 영원히 성립되어서 무한 루프에 빠지게되어 오류가 발생.
코드를 고치려면
std::vector<int>::iterator itr = vec.begin();
//1번째 방법
for (; itr != vec.end(); ++itr) {
if (*itr == 20) {
vec.erase(itr);
itr = vec.begin();
}
}
//2번째 방법
for (std::vector<int>::size_type i = 0; i != vec.size(); i++) {
if (vec[i] == 20) {
vec.erase(vec.begin() + i);
i--;
}
}
int main() {
std::vector vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
// 끝에서 부터 출력하기
for (std::vector::size_type i = vec.size() - 1; i >= 0; i--) {
std::cout << vec[i] << std::endl;
}
return 0;
})
오류의 이유 : vector의 index를 담당하는 타입이 부호없는 정수이기 때문.
따라서, i 가 0일 때 i--를 하게 되면 -1이 아니라 해당 타입에서 가장 큰 정수가 된다. 따라서 for 문이 무한 루프.
연관 컨테이너 (associative container)의 한 종류.
key-value (키 - 값)구조를 가진다. 특정한 키를 넣으면 이에 대응되는 값을 돌려준다는 것.
템플릿 라이브러리 이기 때문에 키와 값 모두 임의의 타입의 객체가 될 수 있다.
Q1. 박명순이 데이터에 존재하나요? (특정 키가 연관 컨테이너에 존재하나요?)
A1. TRUE
Q2. 만약 존재한다면 이에 대응되는 값이 무엇인가요? (특정 키에 대응되는 값이 무엇인가요?)
A2. 46
-Q1의 경우 set과 multiset이고 Q2의 경우 map과 multimap이다.
#include <iostream>
#include <map>
#include <string>
template <typename K, typename V>
void print_map(std::map<K, V>& m) {
// 맵의 모든 원소들을 출력하기
for (auto itr = m.begin(); itr != m.end(); ++itr) {
std::cout << itr->first << " " << itr->second << std::endl;
}
}
int main() {
std::map<std::string, double> pitcher_list;
// 참고로 2017년 7월 4일 현재 투수 방어율 순위입니다.
// 맵의 insert 함수는 pair 객체를 인자로 받습니다.
pitcher_list.insert(std::pair<std::string, double>("박세웅", 2.23));
pitcher_list.insert(std::pair<std::string, double>("해커 ", 2.93));
pitcher_list.insert(std::pair<std::string, double>("피어밴드 ", 2.95));
// 타입을 지정하지 않아도 간단히 std::make_pair 함수로
// std::pair 객체를 만들 수 도 있습니다.
pitcher_list.insert(std::make_pair("차우찬", 3.04));
pitcher_list.insert(std::make_pair("장원준 ", 3.05));
pitcher_list.insert(std::make_pair("헥터 ", 3.09));
// 혹은 insert 를 안쓰더라도 [] 로 바로
// 원소를 추가할 수 있습니다.
pitcher_list["니퍼트"] = 3.56;
pitcher_list["박종훈"] = 3.76;
pitcher_list["켈리"] = 3.90;
print_map(pitcher_list);
std::cout << "박세웅 방어율은? :: " << pitcher_list["박세웅"] << std::endl;
}
//실행 결과
니퍼트 3.56
박세웅 2.23
박종훈 3.76
장원준 3.05
차우찬 3.04
켈리 3.9
피어밴드 2.95
해커 2.93
헥터 3.09
박세웅 방어율은? :: 2.23
- 맵의 경우 템플릿 인자로 2개를 가진다. 첫번째는 키의 타입, 두번째는 값의 타입.
```cpp
std::map<std::string, double> pitcher_list;
template <class T1, class T2>
struct std::pair {
T1 first;
T2 second;
};
- std::pair 객체를 하나하나 전달하는 것은 귀찮다. 그래서 std::make_pair함수를 제공
```cpp
pitcher_list.insert(std::make_pair("차우찬", 3.04));
pitcher_list.insert(std::make_pair("장원준 ", 3.05));
pitcher_list.insert(std::make_pair("헥터 ", 3.09));
template <typename K, typename V>
void print_map(std::map<K, V>& m) {
// 맵의 모든 원소들을 출력하기
for (auto itr = m.begin(); itr != m.end(); ++itr) {
std::cout << itr->first << " " << itr->second << std::endl;
}
}
- 맵에 저장된 값을 찾고 싶다면 간단히 [] 연산자를 이용하면 된다. 하지만 주의할 점이 있다.
```cpp
#include <iostream>
#include <map>
#include <string>
template <typename K, typename V>
void print_map(const std::map<K, V>& m) {
// kv 에는 맵의 key 와 value 가 std::pair 로 들어갑니다.
for (const auto& kv : m) {
std::cout << kv.first << " " << kv.second << std::endl;
}
}
int main() {
std::map<std::string, double> pitcher_list;
pitcher_list["오승환"] = 3.58;
std::cout << "류현진 방어율은? :: " << pitcher_list["류현진"] << std::endl;
std::cout << "-----------------" << std::endl;
print_map(pitcher_list);
}
과 같이 0 이라는 값이 나온다. 없는 값을 참조하면 오류가 발생해야 정상인데? 이는 [] 연산자가 맵에 없는 키를 참조하게 되면, 자동으로 값의 디폴트 생성자를 호출해서 원소를 추가해버리기 때문이다.
따라서, 되도록 find()함수로 키가 존재하는지 확인 후 값을 참조하는 것이 좋다.