c++ 표준 라이브러리(stl)

SOEUN CHOI·2022년 6월 10일
0

C++_study

목록 보기
8/15
post-thumbnail

씹어먹는 C++

11장 c++ 표준 라이브러리(stl) 494p-555p
11장-3, 11장-4 556p - 612p


대표적 C++ STL

  • 컨테이너
    임의 타입의 객체를 보관
  • 반복자
    컨테이너에 보관된 원소에 접근
  • 알고리즘
    반복자들을 가지고 일련의 작업을 수행

컨테이너

시퀀스 컨테이너 (sequence container)

배열 처럼 객체들을 순차적으로 보관
vector, list, deque

  • vector
    일반적인 상황에서는 그냥 벡터를 사용. 거의 만능
  • list
    맨 끝이 아닌 중간에 원소들을 추가하거나 제거 많고
    원소들을 순차적으로만 접근시 사용
  • deque
    맨 처음과 끝 모두에 원소들을 추가 많으면 사용

Vector

원소들이 메모리 상에서 실제로 순차적으로 저장
임의의 위치의 원소를 접근 매우 빠르게 수행

  • 복잡도(complexity)
    O(1)
    임의의 위치의 원소를 접근
    맨 뒤에 새로운 원소를 추가하거나 제거

    amortized O(1)
    미리 더 많은 공간 할당 > 다쓰면 O(n)

  • 접근
    [ ] , at()

  • 위치
    begin() 첫번째 원소
    end() 마지막 원소 +1칸
    빈 벡터를 표현 가능
    begin() == end()

  • 추가 및 제거
    맨 뒤에 원소를 추가하거나 제거
    push_back()/ pop_back()

  • size()
    벡터의 크기를 리턴하는 함수
    값의 타입은 size_type 멤버 타입

example code


int main() {
  std::vector<int> vec;
  vec.push_back(10); // 맨 뒤에 10 추가
  vec.push_back(20); // 맨 뒤에 20 추가
  vec.push_back(30); // 맨 뒤에 30 추가
  vec.push_back(40); // 맨 뒤에 40 추가
  
  for (std::vector<int>::size_type i = 0; i < vec.size(); i++) {
  std::cout << "vec 의 " << i + 1 << " 번째 원소 :: " << vec[i] << std::endl;
  }
}

vec 의 1 번째 원소 :: 10
vec 의 2 번째 원소 :: 20
vec 의 3 번째 원소 :: 30
vec 의 4 번째 원소 :: 40

반복자

RandomAccessIterator
반복자

컨테이너에 iterator 멤버 타입

  • vetcor begin() end()
    std::vector<>::iterator 멤버 타입으로 정의
  • * 연산자 오버로딩
    itr이 가르키는 원소 확인

  • + 연산자 오버로딩
    itr에서 해당 크기만큼 덜어진 원소 가르킴

  • insert erase 사용 가능
    ※ 오류 주의
    erase로 itr를 지우면 해당 값은 유효한 값이 아님 반드시 새로운 값을 넣어줘야 itr을 다시 사용 가능
    디버깅시 주의

  • const_iterator
    const 포인터처럼 가리키고 있는 원소의 값을 바꿀 수 없음

  • 역반복자 (reverse iterator)
    벡터 뒤에서 부터 앞으로 거꾸로 간다는 특징
    (const_reverse_iterator 타입도 있음)
    rbegin() / rend()
    std::vector<int>::reverse_iterator r_iter = vec.rbegin();

example code

#include <iostream>
#include <vector>

template <typename T>
void print_vector(std::vector<T>& vec) {
  // 전체 벡터를 출력하기
  // typename 필요 > iterator 가 std::vector<T>
의 의존 타입
  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);
}

처음 벡터 상태
10
20
30
40
----------------------------
10
20
15
30
40
----------------------------
10
20
15
40

범위 기반 for 문 (range based for loop)

for문 패턴을 매우 간단하게 나타낼 수 있는 방식을 제공

for (/* 원소를 받는 변수 정의 */ : /* 컨테이너 */) { }

example code

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;
  }
}
template <typename T>
void print_vector_range_based(std::vector<T>& vec) {
  // 전체 벡터를 출력하기
  // 범위 기반 for 문 레퍼런스 사용
  for (const auto& elem : vec) {
  	std::cout << elem << std::endl;
  }
}
int main() {
  std::vector<int> vec;
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);
  vec.push_back(4);
  std::cout << "print_vector" << std::endl;
  print_vector(vec);
  std::cout << "print_vector_range_based" << std::endl;
  print_vector_range_based(vec);
  return 0;
}

print_vector
1
2
3
4
print_vector_range_based
1
2
3
4

List

양방향 연결 구조를 가진 자료형

example code

#include <iostream>
#include <list>

int main() {
  std::list<int> lst;
  lst.push_back(10);
  lst.push_back(20);
  lst.push_back(30);
  lst.push_back(40);
  
  for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); ++itr) {
  	std::cout << *itr << std::endl;
  }
}

10
20
30
40

  • 접근
    임의의 위치의 원소에 바로 접근 불가능
    임의의 위치의 원소 접근을 위해 하나씩 링크를 따라가야함

  • 반복자 (BidirectionalIterator)
    ++, -- 연산자는 가능 < 한칸씩 움직인 위치 접근 가능
    + 3, - 5 연산자 불가능 < 임의의 위치 원소 접근 못함

  • 앞뒤의 insert erase 사용
    원소를 추가하거나 제거하는 작업이 O(1) 으로 매우 빠르게 수행됨
    원하는 위치 앞과 뒤에 있는 링크 값만 바꿈

    원소를 지워도 반복자가 무효화 안음
    itr를 계속 사용가능

deque - double ended queue

실행 속도를 위해 메모리를 (많이) 희생하는 컨테이너

  • O(1)으로 수행
    임의의 위치의 원소에 접근
    맨 앞 및 맨 뒤에 원소를 추가/제거 하는 작업
  • 원소들이 실제로 메모리 상에서 연속적으로 존재 않음
    추가적 메모리 필요 < 원소들의 저장 위치 정보 보관 필요
  • 메모리
    일정 크기로 잘려서 각각의 블록 속에 존재
    블록의 메모리 위치 주소 정보 저장을 위해 벡터 사용
    새로 할당 시에 앞쪽 및 뒤쪽 모두에 공간 남겨 사용
#include <deque>
#include <iostream>

template <typename T>
void print_deque(std::deque<T>& dq) {
  // 전체 덱을 출력하기
  std::cout << "[ ";
  for (const auto& elem : dq) {
  	std::cout << elem << " ";
  }
  std::cout << " ] " << std::endl;
}

int main() {
  std::deque<int> dq;
  dq.push_back(10);
  dq.push_back(20);
  dq.push_front(30);
  dq.push_front(40);
  
  std::cout << "초기 dq 상태" << std::endl;
  print_deque(dq);
  std::cout << "맨 앞의 원소 제거" << std::endl;
  dq.pop_front();
  print_deque(dq);
}

초기 dq 상태
[ 40 30 10 20 ]
맨 앞의 원소 제거
[ 30 10 20 ]

연관 컨테이너(associative container)

키(key) - 값(value) 구조
특정한 키를 넣으면 이에 대응되는 값 리턴
키와 값 모두 임의의 타입의 객체 가능

키의 존재 유무 / 대응 되는 값 질의 두가지 타입으로 나뉨
set, mulitset / map, multimap

  • set
    데이터의 존재 유무 만 궁금할 경우

  • multiset
    중복 데이터를 허락할 경우
    insert, erase, find 모두 O(log N). 최악도 O(log N))

  • map
    데이터에 대응되는 데이터를 저장하고 싶은 경우

  • multimap
    중복 키를 허락할 경우
    insert, erase, find 모두 O(log N). 최악도 O(log N))

  • unordered_set, unordered_map
    속도가 매우매우 중요해서 최적화를 해야하는 경우
    insert, erase, find 모두 O(1)
    최악은 O(N)
    그러므로 해시함수와 상자 개수 설정 유의

    셋(set)

    트리 구조로 되어 있음 find 최적화

  • 추가
    insert()
    위치정보 없이 추가
    추가 시 정렬된 상태를 유지 O(logN)

  • 원소
    위치 정보가 아닌 유무가 중요한 정보

  • 반복자 BidirectionalIterator
    순차적 접근만 가능

  • find
    원소의 존재 유무 확인
    정렬된 상태 유지하므로 O(logN)
    존재 시 반복자 리턴
    존재 하지 않을시 s.end() 리턴- 중복
    중복된 원소 추가 불가능
    있는데 추가시 무시함

-원하는 타입 셋 사용
크기 비교가 있어야 사용가능 < 정렬을 사용하기 때문
즉 operater <에 대한 정의로 오버로딩 필요
operator< 조건이 모두 만족해야 접근 가능
또는 함수 객체 사용
const 사용 하여 전달
: 셋 내부적으로 정렬 시에 상수 반복자를 사용

example code

#include <iostream>
#include <set>
template <typename T>
void print_set(std::set<T>& s) {
 // 셋의 모든 원소들을 출력하기
 std::cout << "[ ";
 for (typename std::set<T>::iterator itr = s.begin(); itr != s.end(); ++itr) {
 	std::cout << *itr << " ";
 }
 std::cout << " ] " << std::endl;
}

int main() {
 std::set<int> s;
 s.insert(10);
 s.insert(50);
 s.insert(20);
 s.insert(40);
 s.insert(30);
 
 std::cout << "순서대로 정렬되서 나온다" << std::endl;
 print_set(s);
 
 std::cout << "20 이 s 의 원소인가요? :: ";
 auto itr = s.find(20);
 if (itr != s.end()) {
 	std::cout << "Yes" << std::endl;
 } else {
 	std::cout << "No" << std::endl;
 }
 
 std::cout << "25 가 s 의 원소인가요? :: ";
 itr = s.find(25);
 if (itr != s.end()) {
 	std::cout << "Yes" << std::endl;
 } else {
   std::cout << "No" << std::endl;
 }
}

순서대로 정렬되서 나온다
[ 10 20 30 40 50 ]
20 이 s 의 원소인가요? :: Yes
25 가 s 의 원소인가요? :: No

맵 (map)

키에 대응되는 값(value) 까지도 같이 보관

  • 키와 값
    템플릿 인자 2 개
    첫번째는 키의 타입, 두 번째는 값의 타입

    맵에 원소를 넣기 위해서는 반드시 std::pair 객체를 전달
    make_pair() 사용 < pair 역시 stl (utility 헤더 사용)
    operator[]도 사용가능

  • 반복자
    맵 반복자가 std::pair 객체
    tr->first 키, itr->second 원소

  • 탐색
    키를 통해 원소 확인
    find함수 사용
    itr = m.find(key) itr->second
    find 없는키 참조시 end()리턴
    또는
    []로 맵에 없는 키를 참조
    자동으로 값의 디폴트 생성자를 호출해서 원소를 추가

    중복된 원소 추가 불가능
    있는데 추가시 무시함

example code

#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;
 
  // 맵의 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

멀티셋(multiset)과 멀티맵(multimap)

셋과 맵과는 다르게 중복된 원소를 허락

  • 키와 값
    한 개의 키에 여러개의 값이 대응
    따라서 operator[] 사용 불가능
  • 여러 키
    equal_range 함수 사용
    키에 대응되는 원소들의 반복자들중에서
    시작과 끝 바로 다음을 가리키는 반복자를 std::pair 객체로 리턴
#include <iostream>
#include <map>
#include <string>

template <typename K, typename V>
void print_map(const std::multimap<K, V>& m) {
  // 맵의 모든 원소들을 출력하기
  for (const auto& kv : m) {
  	std::cout << kv.first << " " << kv.second << std::endl;
  }
}

int main() {
  std::multimap<int, std::string> m;
  m.insert(std::make_pair(1, "hello"));
  m.insert(std::make_pair(1, "hi"));
  m.insert(std::make_pair(1, "ahihi"));
  m.insert(std::make_pair(2, "bye"));
  m.insert(std::make_pair(2, "baba"));
  
  print_map(m);
  std::cout << "--------------------" << std::endl;
  // 1 을 키로 가지는 반복자들의 시작과 끝을
  // std::pair 로 만들어서 리턴한다.
  auto range = m.equal_range(1);
  for (auto itr = range.first; itr != range.second; ++itr) {
  	std::cout << itr->first << " : " << itr->second << " " << std::endl;
  }
}

1 hello
1 hi
1 ahihi
2 bye
2 baba
--------------------
1 : hello
1 : hi
1 : ahihi

정렬되지 않은 셋과 맵(unorderedset, unordered-map)

원소가 정렬되어 있지 않음
(셋이나 맵의 경우 원소들이 순서대로 정렬되어서 내부에 저장)
반복자로 원소 출력시 랜덤한 순서로 출력

  • O(1)
    insert, erase, find 모두
    해시 함수로 구현 즉 최악은 O(n)
    최적화가 매우 필요한 작업일 경우에만
    해시 함수를 잘 설계해서 사용해야함
  • 원하는 타입
    원하는 타입으로 사용하고자 하면 직접 해시 함수 구현

해시 함수(Hash function)

임의의 크기의 데이터를 고정된 크기의 데이터로 대응시켜주는 함수
1 부터 D (= 상자의 수)까지의 값을반환하고 그 해시값 (해시 함수로 계산한 값)을 원소를 저장
같은 원소를 해시 함수에 전달한다면 같은 해시값을 리턴

  • 해시 충돌(hash collision)
    다른 원소임에도 불구하고 같은 해시값을 갖는 경우

example code

#include <iostream>
#include <string>
#include <unordered_set>

template <typename K>
void print_unordered_set(const std::unordered_set<K>& m) {
  // 셋의 모든 원소들을 출력하기
  for (const auto& elem : m) {
  	std::cout << elem << std::endl;
  }
}

template <typename K>
void is_exist(std::unordered_set<K>& s, K key) {
  auto itr = s.find(key);
  if (itr != s.end()) {
  	std::cout << key << " 가 존재!" << std::endl;
  } else {
  	std::cout << key << " 가 없다" << std::endl;
  }
} 

int main() {
  std::unordered_set<std::string> s;
  s.insert("hi");
  s.insert("my");
  s.insert("name");
  s.insert("is");
  s.insert("psi");
  s.insert("welcome");
  s.insert("to");
  s.insert("c++");
  
  print_unordered_set(s);
  std::cout << "----------------" << std::endl;
  is_exist(s, std::string("c++"));
  is_exist(s, std::string("c"));
  std::cout << "----------------" << std::endl;
  std::cout << "'hi' 를 삭제" << std::endl;
  s.erase(s.find("hi"));
  is_exist(s, std::string("hi"));
  }

c++
to
my
name
hi
is
psi
welcome
---------------
c++ 가 존재!
c 가 없다
---------------
'hi' 를 삭제
hi 가 없다

C++ 표준 알고리즘 라이브러리

여러 함수로 작업

두종류

알고리즘을 수행할 반복자의 시작점과 끝점 바로 뒤를 인자로 받음
template <typename Iter> void do_something(Iter begin, Iter end);

반복자는 동일하나 ’특정한 조건’ 을 추가 인자를 받음
template <typename Iter, typename Pred> void do_something(Iter begin, Iter end, Pred pred);

’특정한 조건’ - 서술자(Predicate)
Pred 에는 보통 bool 을 리턴하는 함수객체(Functor)

정렬 (sort, stable_sort, partial_sort)

  • sort
    일반적인 정렬 함수라 생각하시면 됩니다.
  • stable_sort
    정렬을 하되 원소들 간의 순서를 보존
  • partial_sort
    배열의 일부분만 정렬합니다

sort

오름차순으로 정렬
sort(vec.begin(), vec.end());

임의접근 반복자(RandomAccessIterator) 타입만 가능
즉 벡터와 데크만 가능

내림 차순으로 정렬을 원하면
compare함수 생성하여 사용
greater 사용도 가능
std::sort(vec.begin(), vec.end(), greater<int>());

example code

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

template <typename Iter>
void print(Iter begin, Iter end) {
  while (begin != end) {
    std::cout << *begin << " ";
    begin++;
  }
  std::cout << std::endl;
}

struct int_compare {
  bool operator()(const int& a, const int& b) const { return a > b; }
};
int main() {
  std::vector<int> vec;
  vec.push_back(5);
  vec.push_back(3);
  vec.push_back(1);
  vec.push_back(6);
  vec.push_back(4);
  vec.push_back(7);
  vec.push_back(2);
  std::cout << "정렬 전 ----" << std::endl;
  print(vec.begin(), vec.end());
  std::sort(vec.begin(), vec.end(), int_compare());
  std::cout << "정렬 후 ----" << std::endl;
  print(vec.begin(), vec.end());
}

정렬 전 ----
5 3 1 6 4 7 2
정렬 후 ----
7 6 5 4 3 2 1

partial_sort

정렬을 [stard, end) 전체 원소들 중에서 [start, middle) 까지 원소들이 전체 원소들 중에서 제일 작은애들 순으로 정렬
std::partial_sort(vec.begin(), vec.begin() + 3, vec.end());

stable_sort

원소가 삽입되어 있는 순서를 보존하는 정렬
std::stable_sort(vec2.begin(), vec2.end());

원소 제거 (remove, remove_if)

컨테이너의 원소를 제거하는 함수

erase

vector의 erase
pos 가 가리키는 원소를 벡터에서 지움
Iterator erase(Iterator pos);
first 부터 last 사이에 있는 모든 원소들을 지움
Iterator erase(Iterator first, Iterator last);

remove

원하는 값을 가진 원소들을 컨테이너 뒤로 보내 지움
즉 지우지 말아야할 원소를 하나씩 앞으로 이동시키는 방식
크기가 지우는게 아니라 땡기는 거다. < 크기가 그대로임
대신 한번에 원하는 값을 갖는 원소 여러개 지우기 가능
보통 erase와 조합하여 사용
시작부터 끝전까지 값 찾아 제거
remove(vec.begin(), vec.end(), 3)
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end());

remove_if

시작부터 끝전까지 조건 충족하면 제거
vec.erase(remove_if(vec.begin(), vec.end(), is_odd()), vec.end());

조건을 위한 함수 객체 사용시 유의
※함수 객체 사용시 주의
객체 내부 변수를 통해절대로 특정 상태를 저장해서 이에 따라 결과가 달라지는 루틴을 짜면 안됨
-> 단순 전달이 아닌 복사나 소멸로 값이 다르게 파악됨

example code

template <typename Iter>
void print(Iter begin, Iter end) {
  while (begin != end) {
    std::cout << "[" << *begin << "] ";
    begin++;
  }
  std::cout << std::endl;
}

struct is_odd {
  bool operator()(const int& i) { return i % 2 == 1; }
};

int main() {
  std::vector<int> vec;
  vec.push_back(5);
  vec.push_back(3);
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);
  vec.push_back(4);
  
  std::cout << "처음 vec 상태 ------" << std::endl;
  print(vec.begin(), vec.end());
  std::cout << "벡터에서 홀수 인 원소 제거 ---" << std::endl;
  vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd()), vec.end());
  print(vec.begin(), vec.end());
}

처음 vec 상태 ------
[5] [3] [1] [2] [3] [4]
벡터에서 홀수 인 원소 제거 ---
[2] [4]

람다 함수(lambda function)

임시적으로 함수 객체 생성

  • 람다 함수도 함수이므로 자기 자신만의 스코프를 갖음
  • 캡쳐 목록(capture list)
    외부 변수를 캡쳐 리스트로 접근 가능
    만약 객체 멤버변수를 원하면 해당 객체를 캡쳐(this처럼)
    []
    아무것도 캡쳐 안함
    [&a, b]
    a 는 레퍼런스로 캡쳐하고 b 는 (변경 불가능한) 복사본으로 캡쳐
    [&]
    외부의 모든 변수들을 레퍼런스로 캡쳐
    [=]
    외부의 모든 변수들을 복사본으로 캡쳐

일반적인 꼴
[capture list] (받는 인자) -> 리턴 타입 { 함수 본체 }
리턴 타입 생략
[capture list] ( 받는 인자) {함수 본체}

example code

//함수 객체
struct is_odd {
  bool operator()(const int& i) { return i % 2 == 1; }
};

//람다 함수
[](int i) -> bool { return i % 2 == 1; }

// 바로 호출
[](int i) { return i % 2 == 1; }(3);// true

//함수객체 성성 후 호출
auto func = [](int i) { return i % 2 == 1; };
func(4); // false;

//캡쳐 리스트 사용
[&num_erased](int i) {
  if (num_erased >= 2)
    return false;
  else if (i % 2 == 1) {
    num_erased++;
    return true;
  }
  return false;
}

// 객체를 캡쳐 리스트로 사용
vec.erase(std::remove_if(vec.begin(), vec.end(),
  [this](int i) {
    if (this->num_erased >= 2)
    	return false;
    else if (i % 2 == 1) {
      this->num_erased++;
      return true;
    }
    return false;
    }),
  vec.end());

원소 수정하기 (transform)

transform
원소들을 수정하는 함수
컨테이너 전체 혹은 일부를 순회하면서 값들을 수정
transform (시작 반복자, 끝 반복자, 결과를 저장할 컨테이너의 시작 반복자, Pred)

  • 저장하는 컨테이너의 크기가 원래의 컨테이너보다 최소한 같거나 커야함

example code

std::transform(vec.begin(), vec.end(), vec.begin(),[](int i) { return i + 1; });

원소를 탐색하는 함수(find, find_if, any_of, all_of...)

원소들을 탐색하는 계열의 함수

find

first 부터 last 까지 순회하며 value와 같은 값 파악 있다면 이를 가리키는 반복자를 리턴
template <class InputIt, class T> InputIt find(InputIt first, InputIt last, const T& value)

  • 중복되는 값이 있더라도 가장 먼저 찾은 것을 리턴
  • 중복 되는 값 찾기
    마지막으로 찾은 위치 바로 다음 부터 계속 순차적으로 탐색
    current = find(current, vec.end(), 3);
  • 만약 해당 컨테이너에 find가 있다면 해당 컨테이너 find 사용

find_if

함수 객체를 인자로 받아서 그 결과가 참인 원소들을 탐색
current = std::find_if(current, vec.end(), [](int i) { return i % 3 == 2; });

any_of

인자로 받은 범위안의 모든 원소들 중에서 조건을 하나라도 충족하는 것이 있다면 true 리턴
OR 연산과 비슷
std::any_of(users.begin(), users.end(), [](User& user) { return user.level >= 19; });

all_of

모든 원소들이 전부 조건을 충족해야 true 리턴
AND 연산과 비슷
std::all_of(users.begin(), users.end(), [](User& user) { return user.level >= 15; });

C++ 문자열(string 과 string_view)

basic_string

basic_string 은 CharT 타입의 객체들을 메모리에 연속적으로 저장하고, 여러가지 문자열 연산들을 지원해주는 클래스

  • 여러 종류 타입으로 인스턴스화된 문자열
    char, wchar_t, char8_t, char16_t, char32_t

  • basic_sting
    문자열들을 어떻게 보관하는지에 대한 로직

  • Traits
    문자열들을 어떻게 연산하는지 에 대한 로직

  • std::char_traits 사용 시
    사용자가 원하는 기능을 가진 문자열을 생성 가능

짧은 문자열 최적화 (SSO)

짧은 길이 문자열의 경우 따로 문자 데이터를 위한 메모리를
할당하는 대신에 그냥 객체 자체에 저장

리터럴 연산자

문자열 리터럴로 정의

std::string operator"" s(const char *str, std::size_t len);
std::string str = "hello"; // char[]

C++ 에서 한글 다루기

인코딩(Encoding) 방식 사용
인코딩 방식에 따라 컴퓨터에서 문자를 표현하기 위해
동일하게 4 바이트를 사용하는 대신에,
어떤 문자는 1 바이트, 2 바이트 등의 다른 길이로 저장

인코딩 방식

  • UTF-8
    문자를 최소 1 부터 최대 4 바이트로 표현
    즉 문자마다 길이가 다르다!
  • UTF-16
    문자를 2 혹은 4 바이트로 표현
  • UTF-32
    문자를 4 바이트로 표현
    메모리 사용량 매우 증가

string_view

문자열을 읽기 만 하는 클래스
어딘가 존재하는 문자열을 참조해서 읽기만 함

  • 메모리 할당 불필요
    읽고 있는 문자열의 시작 주소값만 복사
  • 원본 문자열을 수정하지 않는 연산들 사용 가능
  • 불필요한 복사 발생 하지 않음
    const char* 과 const string&사이에서 깔끔하게 처리
  • Undefined behavior 주의
    현재 보고 있는 문자열이 소멸된다면 정의되지 않은 작업이 발생
    살아 있는 문자열인지 확인이 필요
profile
soeun choi

0개의 댓글