프로그래밍/C,C++

[Modern C++] string_view

GONII 2025. 4. 2. 15:46

1. std::string_view란?

C++17에서 도입된 std::string_view는 문자열을 복사하지 않고 참조하는 경량 래퍼입니다.
기존의 std::string과 const char*의 단점을 보완하며, 문자열을 더 효율적으로 처리할 수 있습니다.

📌 주요 특징:
✔ 문자열을 복사하지 않고 참조 → 빠르고 메모리 절약
✔ std::string, "문자열 리터럴", char* 등과 함께 사용 가능
읽기 전용 (mutable X)

📌 기본 문법:

#include <iostream>
#include <string_view>

void print(std::string_view sv) {  // 복사 없이 문자열 참조
    std::cout << sv << '\n';
}

int main() {
    std::string str = "Hello, World!";
    print(str);               // std::string 전달
    print("Hello, string_view!"); // 문자열 리터럴 전달
}

2. std::string vs. std::string_view 차이점

비교 항목 std::string std::string_view
메모리 할당 문자열을 복사 참조만 유지
크기 동적 할당 작고 가벼움 (sizeof(string_view) == 16 정도)
변경 가능 여부 변경 가능 읽기 전용
종료 문자 (\0) 항상 포함 포함되지 않을 수도 있음
성능 문자열 복사 비용 있음 빠르고 효율적

📌 std::string은 힙에 메모리를 할당하지만, std::string_view는 단순한 포인터와 길이만 저장함.
📌 "임시 문자열을 전달할 때 std::string_view 사용하면 불필요한 복사를 방지할 수 있음.


3. std::string_view 기본 사용법

📌 (1) std::string_view 생성

#include <iostream>
#include <string_view>

int main() {
    std::string_view sv1 = "Hello, World!"; // 문자열 리터럴 사용
    std::string str = "Hello, C++!";
    std::string_view sv2 = str;  // std::string을 참조

    std::cout << sv1 << '\n';
    std::cout << sv2 << '\n';
}

설명:

  • sv1은 문자열 리터럴을 참조.
  • sv2는 std::string을 참조.
  • 문자열 복사가 발생하지 않음.

📌 (2) std::string_view의 주요 메서드

std::string_view는 std::string과 비슷한 메서드를 제공함.

#include <iostream>
#include <string_view>

int main() {
    std::string_view sv = "Hello, string_view!";

    std::cout << "Size: " << sv.size() << '\n';       // 문자열 길이
    std::cout << "First character: " << sv[0] << '\n'; // 문자 접근
    std::cout << "Substring: " << sv.substr(7, 6) << '\n'; // 부분 문자열
    std::cout << "Starts with 'Hello'? " << sv.starts_with("Hello") << '\n';
    std::cout << "Ends with 'view'? " << sv.ends_with("view") << '\n';
}

출력 결과:

Size: 19
First character: H
Substring: string
Starts with 'Hello'? 1
Ends with 'view'? 1

📌 std::string_view는 substr()을 사용하여 부분 문자열을 빠르게 생성 가능.
📌 starts_with() 및 ends_with()를 통해 문자열 접두사/접미사 확인 가능 (C++20)


📌 (3) std::string_view와 std::string 변환

std::string_view → std::string 변환 (복사 발생)

std::string_view sv = "Hello, World!";
std::string s = std::string(sv); // 명시적 변환 필요

📌 변환 시 문자열 복사가 발생하므로 주의!

std::string → std::string_view 변환 (복사 없음)

std::string str = "Hello";
std::string_view sv = str;  // 참조만 유지 (빠름)

📌 std::string_view는 std::string을 참조하므로 복사 비용이 없음.


4. std::string_view 사용 시 주의점

(1) Dangling Reference 문제

std::string_view getView() {
    std::string str = "Temporary";
    return str;  // ❌ Dangling Reference 발생
}

int main() {
    std::string_view sv = getView(); // sv는 해제된 메모리를 가리킴
    std::cout << sv << '\n'; // ❌ 미정의 동작 (UB)
}

해결 방법:

  • std::string이 스코프를 벗어나기 전에 std::string_view를 사용해야 함.
  • 또는 std::string을 직접 반환하여 복사를 허용.

(2) Null-terminated (\0) 보장되지 않음

std::string_view sv = "Hello, World!";
std::cout << sv.data() << '\n';  // 정상

data()는 널 종료 문자 (\0)를 보장하지 않음.
C-스타일 문자열 (char*) 필요하면 std::string으로 변환 후 사용!


5. std::string_view의 활용 예시

📌 (1) 함수 매개변수 최적화

void print(std::string_view sv) {  // 문자열 복사 없음
    std::cout << sv << '\n';
}

int main() {
    std::string str = "Hello";
    print(str);  // std::string 전달
    print("Hello, World!");  // 문자열 리터럴 전달
}

📌 매개변수 타입을 std::string_view로 선언하면 std::string과 "리터럴" 모두 지원 가능.
📌 문자열 복사 비용 절감!


📌 (2) 텍스트 파싱 및 문자열 처리

#include <iostream>
#include <string_view>

void parse(std::string_view sv) {
    while (!sv.empty()) {
        auto pos = sv.find(' ');
        std::cout << sv.substr(0, pos) << '\n';
        if (pos == std::string_view::npos) break;
        sv.remove_prefix(pos + 1);
    }
}

int main() {
    parse("Hello World from string_view!");
}

📌 remove_prefix(n)을 사용하면 문자열 복사 없이 앞부분을 제거 가능.
📌 텍스트 파싱 성능이 향상됨.


🔥 정리

std::string_view는 문자열을 참조하는 가벼운 객체.
문자열 복사를 방지하여 성능 최적화 가능.
읽기 전용이므로 변경 불가능.
매개변수 전달, 텍스트 파싱 등에 활용하면 성능 향상!
Dangling Reference 문제에 주의해야 함.

🚀 C++에서 더 효율적인 문자열 처리를 원한다면? std::string_view를 사용하자! 🚀

반응형