Chapter 2: 스트링과 스트링 뷰 다루기

이 장에서 배울 내용

  • raw string literal
  • C++와 표준 라이브러리의 핵심 요소 문법 둘러보기
  • C++의 std::string 클래스 자세히 들여다보기
  • std::string_view의 용도

C 스타일 스트링과 스트링 리터럴, C++ std::stringstd::string_view(since C++ 17)에 대해서 다루고 있습니다.

C Style String

C언어는 스트링을 단순히 문자 배열로 표현했습니다. 스트링의 마지막에 NUL 문자(\0)를 붙여서 스트링의 마지막을 나타냅니다.

C 스타일 스트링에 관련된 연산은 <cstring> header에서 확인할 수 있습니다.

String Literal

cout << "hello, world!" << endl;

위 예제에서 "hello, world!"처럼 변수에 담지 않고 곧바로 값으로 표현한 스트링을 string literal이라 칭합니다.
스트링 리터럴은 내부적으로 메모리의 읽기 전용 영역에 저장되기 때문에 변경할 수 없습니다. 컴파일러는 내부적으로 동일한 스트링 리터럴이 반복되서 나올 경우 동일한 레퍼런스를 재사용하여 메모리를 절약합니다.

C++에서 스트링 리터럴은 const char이 n개인 배열으로 정의하고 있습니다.
그렇기 때문에 스트링 리터럴을 참조할 때에는 const char*을 사용하는 것이 안전합니다. 스트링 리터럴에 쓰기 작업을 하려는 것을 막아주니까요.

아래와 같이 문자 배열(char[])의 initialization을 진행할때 사용되는 스트링 리터럴은 읽기 전용 메모리에 넣지 않으며 재사용되지도 않습니다.

char arr[] = "hello";

Raw String Literal

const char* str = R"(Hello "world"!
Line2
Line3)";

R"(로 시작해서 )"로 끝나는 raw string literal은 여러 줄에 걸쳐 작성할 수 있고, ", \n, \t 등이 escape sequence로 사용되지 않고 일반 텍스트로 취급됩니다.

Extended Raw String Literal

로 스트링 리터럴은 기본으로 R"()"를 구분자 시퀸스(delimiter sequence)로 사용합니다. 하지만 로 스트링 리터럴 내부에서 R"()"를 사용해야하는 경우가 생길 수도 있지 않을까요?

R"d-char-sequence(blah-blah)d-char-sequence" // = "blah-blah"

위와 같이 임의의 구분자 시퀸스("d-char-sequence( / )d-char-sequence")를 사용하여 스트링 리터럴을 나타내는 것을 extended raw string literal이라 칭합니다. 이때 구분자 시퀸스는 최대 16개의 문자를 사용할 수 있으며 로 스트링 리터럴 안에서 나오지 않는 값이어야 하고 시작과 끝에 똑같이 나와야합니다.

C++ String Literal

표준 사용자 정의 리터럴 s를 사용하면 스트링 리터럴을 std::string으로 만들 수 있습니다.

auto str1 "Hello World!"; // const char*
auto str2 "Hello World!"s; // std::string

std::string_view Class (Since C++17)

읽기 전용(read-only) 스트링을 받는 함수나 메서드의 매개변수 타입은 const std::string&이나 const char* 대신 std::string_view를 사용하는 것이 좋습니다.

Rust, Python, Java 등 String 클래스가 기본으로 사용되는 여타 프로그래밍 언어들과 달리 C++는 태생적으로 C 스타일 스트링(문자 배열)과의 호환성을 고려해야 했기 때문에 여러가지 귀찮음이 존재했습니다.
그 중 하나가 읽기 전용(read-only) 스트링을 받는 함수의 매개변수 타입을 결정하는 일입니다. const char*로 지정할 경우 std::string을 사용하는 클라이언트는 c_str()이나 data()를 이용하여 stringconst char*로 변환해서 호출해야 합니다. 이 경우 std::string의 이점인 헬퍼 메서드를 사용할 수 없게 됩니다.
반대로 const std::string&로 지정할 경우 std::string만 사용해야 합니다. 스트링 리터럴을 지정할 경우 새로운 string 객체를 생성해서 함수로 전달하는 오버헤드가 발생합니다.

C++17에서 추가된 std::string_view 클래스를 사용하여 이러한 문제를 해결할 수 있습니다. <string_view> 헤더에 정의되어 있으며 const std::string& 대신 사용되고 스트링이 복사되지 않기 때문에 오버헤드가 없습니다.

또한 스트링 뷰는 스트링에 대한 포인터와 길이만 가지고 있기 때문에 pass-by-value로 값이 복사되어도 효율적입니다.

string_view는 자동으로 string으로 형변환 되지 않습니다. 스트링 생성자를 호출하거나 string_view::data()로 생성해야합니다.

Example

string_view extractExtension(string_view fileName) {
    return fileName.substr(fileName.rfind('.'));
}

Back to 전문가를 위한 C++ (Professional C++) 작성 포스트 모음