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;
}
'C, C++ > 표준 라이브러리' 카테고리의 다른 글
[C++] std::tuple 사용법 (0) | 2024.08.09 |
---|---|
[C++] transform 사용법 (0) | 2024.08.03 |
[C++] for_each 함수 사용법 (0) | 2024.07.26 |
[C++] 함수 포인터를 확장한 std::function 사용법 (0) | 2024.07.26 |
[C++] STL lower_bound와 upper_bound 함수 사용법 (0) | 2024.06.26 |