----------------
title : C++ 레퍼런스 - string 생성자
cat_title :  string 생성자
ref_title : string, wstring, std::string, std::wstring, std::u16string, std::u32string
path : /C++ Reference/string
publish_date : 2020-07-17
----------------

#@ string::string

`basic_string` 의 경우 여러가지 형태의 생성자를 지원하고 있습니다. 특히 생성자를 잘 이용하면 코드를 더 깔끔하게 쓸 수 도 있기 때문에 상당히 유용합니다.

또한, 생성자 종류가 매우 많지만, 그 큰 틀은 다 비슷하기 때문에 (생성자 뿐만이 아니라 C++ STL 라이브러리의 모든 함수들이 비슷한 형태를 취하고 있습니다.) 크게 어려움 없이 숙지하실 수 있습니다.

참고로 직접 할당자를 만들어서 사용하지 않는 이상 `Allocator` 가 무엇 인지는 크게 고민하지 않으셔도 됩니다. 대부분의 경우 디폴트 할당자를 사용할 것입니다.

### 생성자 오버로딩 종류

```cpp-formatted
basic_string() noexcept(noexcept(Allocator())) : basic_string(Allocator()) {}
explicit basic_string(const Allocator& alloc) noexcept;
```

빈 문자열을 생성한다.

```cpp-formatted
basic_string(size_type count, CharT ch, const Allocator& alloc = Allocator());
```

`count` 만큼의 문자 `ch` 를 가진 문자열을 생상한다. 같은 문자가 반복되는 문자열을 생성할 때 사용하면 편리하다.

```cpp-formatted
basic_string(const basic_string& other, size_type pos,
             const Allocator& alloc = Allocator());
basic_string(const basic_string& other, size_type pos, size_type count,
             const Allocator& alloc = Allocator());
```

인자로 전달된 문자열 `other` 의 부분문자열로 문자를 생성한다. 첫 번째 경우, `other` 의 `pos` 부터 끝까지를 문자열로 생성하고, 두 번째의 경우 `pos` 부터 `count` 만큼을 택해서 문자열을 생성한다. `substr` 함수의 작동 원리와 동일하다.

```cpp-formatted
basic_string(const CharT* s, size_type count,
             const Allocator& alloc = Allocator());
```

s 의 첫 `count` 문자만큼을 취해서 문자열을 생성한다. 만약에 s 가 가리키는 문자열의 길이가 `count` 보다 작다면 **어떤식으로 작동해야 할지 정의되어 있지 않다.**\sidenote{이를 undefined behavior 라고 한다} 따라서 반드시 s 의 길이가 `count` 이상 인지 확인해야 한다.

```cpp-formatted
basic_string(const CharT* s, const Allocator& alloc = Allocator());
```

널 종류 문자열 s 를 복사해서 문자열을 생성한다.

```cpp-formatted
template <class InputIt>
basic_string(InputIt first, InputIt last, const Allocator& alloc = Allocator());
```

`first` 부터 `last` 바로 전 까지의 문자들을 취해서 문자열을 생성한다.

```cpp-formatted
basic_string(const basic_string& other);
basic_string(const basic_string& other, const Allocator& alloc);
basic_string(basic_string&& other) noexcept;
basic_string(basic_string&& other, const Allocator& alloc);
```

`basic_string` 의 복사 생성자와 이동 생성자들.

```cpp-formatted
basic_string(std::initializer_list<CharT> ilist,
             const Allocator& alloc = Allocator());
```

초기화자 리스트(initializer list) `ilist` 로 부터 문자열을 생성한다.

```cpp-formatted
template <class T>
explicit basic_string(const T& t, const Allocator& alloc = Allocator());
```

`t` 를 `string_view sv` 로 변환 한 뒤에 (`std::basic_string_view<CharT, Traits> sv = t`),
`sv` 로 부터 문자열을 초기화 한다 (이는 `basic_string(sv.data(), sv.size(), alloc)` 와 동일함). 따라서 `T` 는 반드시 `string_view` 로 변환 가능한 타입이어야만 한다.

```cpp-formatted
basic_string(const T& t, size_type pos, size_type n,
             const Allocator& alloc = Allocator());
```

`t` 를 `string_view sv` 로 변환한 뒤에 (`std::basic_string_view<CharT, Traits> sv = t`), `pos` 부터 `n` 개의 문자들로 문자열을 초기화 한다 (`basic_string(sv.substr(pos, n), a)` 와 동일). 이 경우 역시 `T` 가 `string_view` 로 변환 가능한 타입이어야 한다.

### 인자들

* `alloc`	-	문자열의 할당을 담당하는 할당자. 명시하지 않을 경우 디폴트 할당자가 사용된다.
* `count`	-	만들어지는 문자열의 길이
* `ch`	-	초기화 시에 어떤 문자를 사용할 것인지.
* `pos`	-	사용할 첫 번째 문자의 위치
* `first, last`	-	읽어들일 문자의 범위 (`last` 는 읽어들이지 않는다.)
* `s`	-	문자열 초기화를 위해 읽어들일 문자 배열을 가리키는 포인터
* `other`	-	문자열 초기화를 위해 읽어들일 다른 문자열
* `ilist`	-	초기화자 리스트(`std::initializer_list`)
* `t`	-	`std::basic_string_view` 로 변환 가능한 객체로, 이를 바탕으로 문자열을 초기화 한다.

### 복잡도

이동 생성자를 제외한 나머지 모두의 경우, 생성되는 문자열의 길이에 비례한다. 즉 $$O(n)$$, $$n$$ 은 문자열의 길이

### 주의 할 점

'\0' (널 문자열) 를 포함하고 있는 문자열로 초기화 할 시에, 주의해야 할 점이 있다.

```cpp-formatted
std::string s1 = "ab\0\0cd";    // s1 contains "ab"
std::string s2{"ab\0\0cd", 6};  // s2 contains "ab\0\0cd"
```

위 `s1` 의 경우, `basic_string( const CharT* s, const Allocator& alloc = Allocator())` 꼴의 생성자가 오버로딩 되서, 뒤에 오는 `\0cd` 를 무시하게 된다. (첫 번째 NULL 문자를 만날 때 까지 만 읽기 때문에).

반면에 `s2` 의 경우, `basic_string( const CharT* s, size_type count, const Allocator& alloc = Allocator());` 꼴의 생성자가 오버로딩 되서, `NULL` 을 만나느냐의 유무와 상관없이 정해진 길의 문자열 (위 경우 6) 을 읽기 때문에 `s1` 과 같은 문제는 생기지 않는다.

`s1` 과 같은 문제를 막기 위해서는 리터럴 연산자인 `operator""s` 를 사용해서, 문자열을 있는 그대로 읽을 수 있게 된다.

```cpp-formatted
std::string s3 = "ab\0\0cd"s;  // s3 contains "ab\0\0cd"
```

```cpp-formatted
#include <cassert>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>

int main() {
  {
    // string::string()
    std::string s;
    assert(s.empty() && (s.length() == 0) && (s.size() == 0));
  }

  {
    // string::string(size_type count, charT ch)
    std::string s(4, '=');
    std::cout << s << '\n';  // "===="
  }

  {
    std::string const other("Exemplary");
    // string::string(string const& other, size_type pos, size_type count)
    std::string s(other, 0, other.length() - 1);
    std::cout << s << '\n';  // "Exemplar"
  }

  {
    // string::string(charT const* s, size_type count)
    std::string s("C-style string", 7);
    std::cout << s << '\n';  // "C-style"
  }

  {
    // string::string(charT const* s)
    std::string s("C-style\0string");
    std::cout << s << '\n';  // C 스타일 문자열 (널 종료 문자열)
  }

  {
    char mutable_c_str[] = "another C-style string";
    // string::string(InputIt first, InputIt last)
    std::string s(std::begin(mutable_c_str) + 8, std::end(mutable_c_str) - 1);
    std::cout << s << '\n';  // C 스타일 문자열 (널 종료 문자열)
  }

  {
    std::string const other("Exemplar");
    std::string s(other);
    std::cout << s << '\n';  // "Exemplar"
  }

  {
    // string::string(string&& str)
    std::string s(std::string("C++ by ") + std::string("example"));
    std::cout << s << '\n';  // "C++ by example"
  }

  {
    // string(std::initializer_list<charT> ilist)
    std::string s({'C', '-', 's', 't', 'y', 'l', 'e'});
    std::cout << s << '\n';  // "C-style"
  }

  {
    // 재미 있게도, 아래 함수는 string(InputIt first, InputIt last) 버전으로
    // 오버로딩 된다 (InputIt 가 int 인 것으로). 하지만, 이 경우 이 오버로딩
    // 꼴은 마치 string(size_type count, charT ch) 가 호출된 것 같이 작동한다.
    std::string s(3, std::toupper('a'));
    std::cout << s << '\n';  // "AAA"
  }
}
```

실행 결과

```exec
====
Exemplar
C-style
C-style
C-style string
Exemplar
C++ by example
C-style
AAA
```

### 참고 자료

* `assign` - 문자열에 문자를 대입한다.
* `operator=` - 문자열의 문자열을 대입한다.