[C++] string_view에 대한 설명과 사용법

string_view

std::string_view는 문자열을 사용하면서 발생하는, 무거운 복사 과정을 줄이고자 만든 클래스입니다.

이 클래스는 실제 원본 문자열을 읽기 전용 방식으로 참조하는 기능을 합니다.

이 클래스는 C++ 17 이상의 버전에서 사용이 가능합니다.

 

strint_view가 동작하는 방법은 다음의 비유를 통해 이해할 수 있을 것입니다.

 

만약, 자동차를 그리고 싶다면 두 가지 방법이 있습니다.

하나는 자동차를 사서 세워두고 그리는 방법입니다. 비쌉니다.

다른 하나는 남이 세워둔 자동차를 창 너머에서 그리는 방법입니다.

이 방법은 남의 차니까 조심스럽지만, 많은 비용이 들지 않습니다.

 

위의 두 번째 방법이 string_view가 문자열을 다루는 방법입니다.

string_view는 C-style 문자열, std::string 그리고 다른 string_view로부터 생성할 수 있습니다.

이때, 문자열 복사를 전혀 하지 않습니다.

 

string_view를 사용하려면 먼저 다음의 헤더 파일을 포함해야 합니다.

#include <string_view>

 

string_view를 생성하는 예제입니다.

#include <iostream>
#include <string>
#include <string_view>
using namespace std;

int main(){

    string_view sv = "Hello, C-style string";   // c-style 문자열로 초기화

    string str("C++ string");
    string_view sv2(str);   // string를 사용해서 초기화

    string_view sv3( sv2);  // string_view를 사용해서 초기화

    cout << "sv : " << sv << endl;
    cout << "sv2: " << sv2 << endl;
    cout << "sv3: " << sv3 << endl;
}

▼출력

sv : Hello, C-style string
sv2: C++ string
sv3: C++ string

 

string_view의 암시적 변환

C-style 문자열과 std::string은 모두 string_view로 암시적 변환을 할 수 있습니다.

따라서, 다음의 코드는 제대로 동작합니다.

void print_string_view( string_view sv){

    cout << sv << endl;
}

int main(){

    char cStr[] = "Hello, C-style string"; 
    print_string_view(cStr);

    string str("C++ string");
    print_string_view(str);  // str 복사가 발생하지 않음
}

 

하지만, string_view를 std::string으로 암시적인 변환을 할 수는 없습니다.

void print_string( string str){

    cout << str << endl;
}

int main(){

    string_view sv = "Hello, C string";
    //print_string(sv);   // error !
    
    string str{ sv };   // 명시적 변환
    print_string(str);

    string str2 = static_cast<string>(sv);  // static_cast
}

따라서, string을 출력하려면, 먼저 string_view를 string으로 명시적 변환을 해야 합니다.

아니면, string_view를 static_cast를 통해서 string으로 변환하는 방법도 있습니다.

 

이때, 문자열 복사가 일어납니다.

그리고, print_string 호출 시, 이 경우 다시 한번 더 문자열 복사가 일어나게 됩니다.

 

string_view의 수정

string_view는 참조하고 있는 대상 문자열을 수정할 수 없습니다.

그렇지만, 참조하고 있는 대상 문자열의 일부분만을 참조할 수 있습니다.

 

이것은, 창 밖에 있는 자동차를 볼 때, 커튼을 움직여 보는 범위를 조정하는 것과 같습니다.

물론, 자동차는 절대로 줄어들지 않습니다.

 

이러한 기능을 하는 함수가 remove_prefix, remove_surfix, substr입니다.

constexpr void remove_prefix(size_type n);

remove_prefix는 문자열 왼쪽 n개의 문자를 감춥니다.

 

constexpr void remove_suffix(size_type n);

remove_surffix는 문자열 오른쪽 n개의 문자를 감춥니다.

 

constexpr string_view substr(size_type offset = 0, size_type count = npos) const;

substr은 offset부터 count개의 문자를 보여주는 string_view를 반환합니다.

여기서 npos의 값은 -1입니다.

이 경우, offset 인덱스부터 문자열 마지막까지 참조하게 됩니다.

 

이들을 사용하는 예제는 다음과 같습니다.

int main(){

    string str( "merry christmas");

    string_view sv = str;
    sv.remove_prefix(6);
    cout << sv << endl;

    sv = str;   // reset
    sv.remove_suffix(10);
    cout << sv << endl;

    sv = str;   // reset
    sv = sv.substr( 6, 6);
    cout << sv << endl;
}

▼출력

christmas
merry
christ

 

string_view와 const string& 의 차이점

함수의 매개 변수로 string_view과  const string&의 차이점을 느낄 수 없을지도 모릅니다.

그렇지만, 일반적으로 string_view가 여러 경우에 우수합니다.

 

먼저, const string& 타입의 매개 변수를 사용한다고 생각해 봅시다.

그럼, C-style의 문자열을 입력받을 때, 문자열 복사가 발생합니다.

무거운 비용이 발생합니다.

그리고, string_view 문자열을 입력받아도 복사가 필요합니다.

 

반대로, string_view 타입의 매개 변수를 사용한다고 가정하면,

C-style의 문자열을 입력할 때, 문자열 복사를 하지 않습니다.

그리고, str::string 문자열도 마찬가지로 복사가 발생하지 않습니다.

마지막으로, string_view를 입력해도 당연히 문자열을 복사하지 않습니다.

 

이를 정리하면 다음과 같습니다.

  C-style string std::string string_view
const string& 고비용 저비용 고비용
string_view 저비용 저비용 저비용

 

하지만, string_view가 항상 좋은 것은 아닙니다.

string_view를 매개 변수로 받는 함수에서, const string&을 매개 변수로 받는 다른 함수를 호출할 수 있습니다.

그럴 경우, string_view는 std::string으로 변환해야 하고, 이 결과를 다음 함수에 전달해야 합니다.

이 과정에서 복사가 필요합니다.

 

하지만, const string& 매개 변수의 경우, 복사할 필요가 없습니다.

그리고, C++ 14 이하의 버전을 사용하는 경우, string_view를 사용할 수 없습니다.

 

string_view 사용 시, 주의할 사항

string_view는 실제 원본 문자열( C-style 문자열, std::string)을 읽기 전용으로 참조하는 객체입니다.

따라서, 원본 문자열이 파괴되면, string_view 객체가 수행하는 동작은 예측할 수 없습니다.

 

이것은 참조 변수가 다른 객체를 참조할 때의 주의 사항과 마찬가집니다.

모든 참조 방식을 가지는 객체는 항상 원본 객체의 생존 여부에 민감해야 합니다.

int main(){

    string_view sv;

    {
        string str("C++ string");
        sv = str;
    }

    cout << "string view: " << sv << endl;	// 정의되지 않은 행동
}

위의 코드에서, 문자열 str은 코드 블록의 끝에서 파괴됩니다.

따라서, sv의 행동의 정의되지 않습니다.

 

다음의 예제도 마찬가지로 정의되지 않은 동작을 하게 됩니다.

string Create_String( const char* pstr){
    return string(pstr);
}

int main(){

    string_view sv;
    sv = Create_String("Hello");
    
    cout << "string view: " << sv << endl;
}

Create_String에서 반환된 임시 문자열 객체는 sv를 대입된 후에 바로 삭제됩니다.

따라서, sv를 출력하는 동작은 정의되지 않습니다.

이렇게, string_view가 참조하는 문자열이 파괴되는 것에 주의해야 합니다.

 

또한, 원본 문자열이 수정되어도, string_view는 대상을 잘못 참조하는 것이 됩니다.

int main(){

    string_view sv;

    string str("C++ string");
    sv = str;
    
    str = "string was changed"; // 원본 문자열의 변경

    cout << "string view: " << sv << endl;	// 정의되지 않은 행동
}

위의 예제에서, 원본 문자열이 변경되었습니다.

이 경우 sv의 동작은 정의되지 않습니다.

따라서, sv에 str을 다시 대입해, 참조하는 대상을 갱신해야 합니다.

int main(){

    string_view sv;

    string str("C++ string");
    sv = str;
    
    str = "string was changed"; // 원본 문자열의 변경
    sv = str;  // 참조 대상 갱신

    cout << "string view: " << sv << endl;
}

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유