2장 스트링과 스트링 뷰 다루기(1)

myeongrangcoding·2023년 11월 28일
0

전문가를 위한 C++

목록 보기
3/8

C 스타일 스트링

  • 널 문자에 대한 공식 기호: NUL.(포인터에 쓰는 NULL과는 다른 값이다.)
  • 가장 좋은 방법은 C++ 표준 라이브러리에서 제공하는 std::string 클래스를 사용하는 것이다.

스트링 리터럴

  • 메모리의 읽기 전용 영역(rodata)에 저장된다.
  • 리터럴 풀링: "hello"란 스트링 리터럴을 500번 넘게 작성해도 컴파일러는 실제 메모리 공간을 딱 하나만 할당한다.
  • 문자 배열(char[])의 초깃값을 설정할 때도 스트링 리터럴을 사용한다. 이때 컴파일러는 이렇게 만든 스트링 러터럴을 읽기 전용 메모리에 넣지 않으며 재활용하지도 않는다.

로 스트링 리터럴

  • 여러 줄에 걸쳐 작성하는 스트링 리터럴.
  • 인용 부호를 이스케이프 시퀀스로 표현하지 않아도 된다.
const char* str{ R"(Line 1
Line2)"};

// 출력.
// Line 1
// Line 2
  • 로 스트링 리터럴은 )"로 끝나기 때문에 이 구문을 사용하는 스트링 안에 )"를 넣을 수 없다.
  • )" 문자를 추가하려면 확장 로 스트링 리터럴 구문으로 표현해야 한다.
    R"구분자(로 스트링)구분자"
const char* str {R"-(Embedded )" characters)-"}'

C++ std::string 클래스

  • 연산자 오버로딩 덕분에 쓰기 편하다.
  • string 객체는 모두 스택 변수로 생성되기 때문에 스트링을 할당하거나 크기를 조절하는 코드가 여러 군데 흩어져 있어도 메모리 누수가 발생하지 않는다.
  • c_str() 메서드: C 스트링을 표현하는 const 포인터를 리턴한다.
  • data() 메서드: 비 const 스트링에 대해 호출하면 char*를 리턴한다.

스트링 연산

  • substr(pos, len): 시작 위치와 길이에 맞는 서브스트링 반환.
  • find(str): str이 있는 지점을 리턴한다. 없다면 string::npos를 리턴한다.
  • replace(pos, len, str): 시작 위치와 길이에 해당하는 부분을 str로 교체한다.
  • starts_with(str)/ends_with(str): str로 시작하거나 끝나면 true를 리턴한다.(C++20)
  • C++20부터 std::string은 constexpr 클래스다. 즉, string은 컴파일 시간에 연산을 수행하는 데 사용할 수 있으며, constexpr 함수와 클래스 구현에 활용할 수 있다.

std::string 리터럴

auto string1 {"Hello World"};	// string1은 const char*.
auto string2 {"Hello World"s};	// string2는 std::string.

숫자 변환

  • 하이레벨 함수와 로우레벨 함수.

하이레벨 숫자 변환

  • to_string(T val): val을 string으로 변환.
  • stoi(const string& str, size_t *idx = 0, int base = 10), ...
    변환하려는 string 값 str, 변환되지 않은 부분의 맨 앞에 있는 문자의 인덱스를 가리키는 포인터 idx, 변환할 수의 밑 base
    제일 앞에 나온 공백 문자 무시, 변환에 실패하면 invalid_argument 익셉션을 던지고, 변환된 값이 리턴 타입의 범위를 벗어나면 out_of_range 익셉션을 던진다.

로우레벨 숫자 변환

  • <charconv> 헤더에 정의.
  • 호출한 측에서 원시 버퍼를 할당하는 방식으로 사용해야 한다.
  • 로케일에 독립적이고 퍼펙트 라운드 트리핑을 지원하면서 빠른 속도로 처리하고 싶다면 로우레벨 함수를 사용한다.
    • 퍼펙트 라운드 트리핑: 숫잣값을 스트링 형태로 직렬화한 뒤, 그 결과로 나온 스트링을 다시 숫잣값으로 역직렬화하면 원래 값이 나온다.
struct to_chars_result
{
	char* ptr;
    errc ec;
};

enum class chars_format
{
	scientific,
    fixed,
    hex,
    general = fixed | scientific
};

// 숫자를 스트링으로 변환하는 함수.
to_chars_result to_chars(char* first, char* last, IntergetT value, int base = 10);

// 부동소수점 타입에 대한 변환 함수.
to_chars_result to_chars(char* first, char* last, FloatT value);
to_chars_result to_chars(char* first, char* last, FloatT value, chars_format format);
to_chars_result to_chars(char* first, char* last, FloatT value, chars_format format, int precision);
struct from_chars_result
{
	const char* ptr;
    errc ec;
};

// 문자 시퀀스를 숫잣값으로 변환하는 함수.
from_chars_result from_chars(const char* first, const char* last, IntergetT& value, int base = 10);
from_chars_result from_chars(const char* first, const char* last, FloatT& value, chars_format format = chars_format::general);

std::string_view 클래스

  • <string_view> 헤더에 정의.
  • 함수의 매개변수로 읽기 전용 스트링을 받을 때는 const string&나 const char* 대신 std::string_view를 사용한다.
  • string_view를 사용하는 것만으로는 string이 생성되지 않기 때문에 string과 string_view를 서로 결합할 수 없다.
    • data() 메서드나 append()를 사용해야 한다.
string str {"Hello"};
string_view sv {" World"};
auto result {str + sv};					// 불가능.
auto result {str + sv.data()};			// 가능.

string result2 {str};
result2.append(sv.data(), sv.size());	// 가능.
  • 스트링을 리턴하는 함수에서는 string_view로 리턴하면 안 된다. 이것이 가리키던 본래 스트링을 다시 할당할 경우 string_view가 무효로 될 위험이 있기 때문이다.
  • 클래스 데이터 멤버는 이들이 가리키는 스트링이 객체의 수명 동안 살아 있도록 보장해야 하기 때문에 std::string으로 저장하는 것이 안전하다.

std::string_view와 임시 스트링

  • string_view는 임시 스트링에 대한 뷰를 저장하는 용도로 사용하면 안 된다.
  • sv에 대한 초기자 표현식s + "World"로부터 임시 스트링이 생성되고 sv는 생성된 임시 스트링에 대한 포인터를 저장한다. 그러므로 두 번째 문장을 실행하고 나서 이 임시 스트링이 삭제되고 나면 sv는 댕글링 포인터가 되어버린다.
string s{ "Hello" };
string_view sv{ s + " World" };
cout << sv << endl;

// 출력
// ello World

std::string_view 리터럴

  • 표준 사용자 정의 리터럴인 sv를 사용하면 스트링 리터럴을 std::string_view로 만들 수 있다.
auto sv {"Hello World"sv};
profile
명랑코딩!

0개의 댓글