개인 기록용
-------------------
학습에 참고한 사이트 : learncpp.com
학습한 챕터 : 챕터 5
학습에 참고한 번역 프롬프트 : 해당 원문은 learncpp.com에서 발췌해온 c++ 강의 내용인데, 프로그래밍 용어는 한국어 표준 용어를 사용하되, 이해를 돕기 위해 필요한 경우 원문을 병기해서 한국어로 번역해줘.
- 프로그래밍에서 상수(constant)는 프로그램 실행 중에 변경할 수 없는 값을 의미합니다.
- C++는 두 가지 유형의 상수를 지원합니다.
이름 있는 상수 (Named constants)
- 식별자(identifier)와 연결된 상수 값입니다. 때때로 심볼릭 상수(symbolic constants)라고도 불립니다.
리터럴 상수 (Literal constants)
- 식별자와 연결되지 않은 상수 값입니다.
이름 있는 상수의 종류
C++에서 이름 있는 상수를 정의하는 방법은 세 가지가 있습니다.
- 상수 변수 (Constant variables)
- 치환 텍스트가 있는 객체 유사 매크로
- 열거형 상수
상수 변수가 가장 흔하게 사용되는 이름 있는 상수입니다.
상수 변수 (Constant variables)
- 상수 변수(constant variable)는 초기화된 후에는 값을 변경할 수 없는 변수를 말합니다.
const 변수 선언하기
- 상수 변수를 선언하려면 객체의 타입 옆에 const 키워드를 붙이면 됩니다.
- Const 변수는 정의할 때 반드시 초기화해야 하며, 그 이후에는 대입을 통해 값을 변경할 수 없습니다.
- 함수 매개변수도 const 키워드를 사용하여 상수로 만들 수 있습니다.
- 함수 매개변수를 상수로 만들면 함수 내부에서 매개변수의 값이 변경되지 않도록 컴파일러의 도움을 받을 수 있습니다. 하지만 현대 C++에서는 값 매개변수를 굳이 const로 만들지 않습니다. 왜냐하면 함수가 매개변수의 값을 바꾸더라도 호출자에게는 영향을 주지 않기 때문입니다(함수 종료 시 파괴되는 복사본일 뿐이니까요).
- 함수의 반환값)의 경우에도, 값으로 const 객체를 반환하는 것은 별 의미가 없습니다. 어차피 파괴될 임시 복사본이기 때문입니다. 또한 const 값을 반환하면 이동 시맨틱(move semantics)과 관련된 특정 컴파일러 최적화를 방해하여 성능 저하를 일으킬 수 있습니다.
변수를 상수로 만들어야 하는 이유
- 버그 발생 가능성을 줄입니다.
- 컴파일러 최적화 기회를 제공합니다.
- 프로그램의 전체적인 복잡성을 줄여줍니다. 코드를 분석하거나 디버깅할 때, const 변수는 값이 변하지 않는다는 것을 알기 때문에 값이 실제로 변하는지, 어떤 값으로 변하는지, 그 값이 올바른지 걱정할 필요가 없습니다.
치환 텍스트가 있는 객체형 매크로 (Object-like macros)
- 챕터 2에서 치환 텍스트가 있는 객체형 매크로에 대해 논의했습니다.
#include <iostream>
#define MY_NAME "Alex"
int main()
{
std::cout << "My name is: " << MY_NAME << '\n';
return 0;
}
전처리기가 이 코드가 포함된 파일을 처리할 때, MY_NAME(7번째 줄)을 "Alex"로 치환합니다.
MY_NAME은 이름이고 치환 텍스트는 상수 값이므로, 이는 이름 있는 상수(named constants)의 한 형태입니다.
그렇다면 왜 이름 있는 상수로 치환 텍스트가 있는 객체형 매크로를 사용하면 안 될까요?
적어도 세 가지의 큰 문제가 있습니다.
- 가장 큰 문제는 매크로가 일반적인 C++ 스코프(scoping) 규칙을 따르지 않는다는 점입니다. 매크로가 한 번 #defined 되면, 해당 파일의 나머지 부분에서 나오는 모든 매크로 이름이 치환됩니다. 다른 곳에서 같은 이름을 변수명 등으로 사용하고 있다면 원치 않는 치환이 발생하여 이상한 컴파일 오류로 이어질 수 있습니다.
- 두 번째로, 매크로를 사용한 코드는 디버깅하기가 더 어렵습니다.
- 세 번째로, 매크로 치환은 C++의 다른 문법들과 다르게 동작하므로 부주의한 실수를 하기가 쉽습니다.
타입 한정자 (Type qualifiers)
- 타입 한정자(type qualifier)(줄여서 한정자)는 타입에 적용되어 그 타입의 동작 방식을 수정하는 키워드입니다.
- const가 바꾸는 것: “수정 가능 여부”
- 상수 변수를 선언할 때 사용하는 const를 const 타입 한정자(const type qualifier) (줄여서 const 한정자)라고 합니다.
- C++23 기준으로, C++에는 두 가지 타입 한정자만 존재합니다. const와 volatile
선택적 읽기 (Optional reading)
- volatile 한정자는 객체의 값이 언제든지(컴파일러가 예측할 수 없는 방식으로) 변경될 수 있음을 컴파일러에게 알리는 데 사용됩니다.
- 거의 사용되지 않는 이 한정자는 특정 유형의 최적화를 비활성화합니다.
- 기술 문서에서는 const와 volatile 한정자를 종종 cv-qualifiers(cv-한정자)라고 부릅니다.
- cv-unqualified type (cv-비한정 타입):
- 타입 한정자가 없는 타입
- 예: int
- cv-qualified type (cv-한정 타입)
- 하나 이상의 타입 한정자가 적용된 타입
- 예: const int
- possibly cv-qualified type (잠재적 cv-한정 타입)
- cv-비한정이거나 cv-한정일 수 있는 타입
리터럴 접미사 (Literal suffixes)
- 리터럴의 기본 타입이 원하는 타입이 아니라면, 접미사(suffix)를 추가하여 리터럴의 타입을 변경할 수 있습니다.
- 다음은 자주 사용되는 접미사들입니다:
문자열 리터럴 (String literals)
- 프로그래밍에서 문자열(string)은 텍스트(이름, 단어, 문장 등)를 표현하는 데 사용되는 순차적인 문자의 집합입니다.
- C 언어로부터 물려받았기 때문에 종종 C 문자열(C strings) 또는 C 스타일 문자열(C-style strings)이라고 불립니다.
- 모든 C 스타일 문자열 리터럴에는 암시적인 널 종결자(implicit null terminator)가 있습니다.
- C 스타일 문자열 리터럴은 프로그램 시작 시 생성되어 프로그램 전체 실행 기간 동안 존재함이 보장되는 const 객체입니다.
- C 스타일 문자열 리터럴과 달리, std::string과 std::string_view 리터럴은 임시 객체(temporary objects)를 생성합니다.
- 이 임시 객체들은 생성된 전체 표현식(full expression)이 끝날 때 파괴되므로 즉시 사용되어야 합니다.
매직 넘버 (Magic numbers)
- 매직 넘버(Magic number)는 의미가 불분명하거나 나중에 변경될 필요가 있는 리터럴(주로 숫자)을 말합니다.
- 매직 넘버를 사용하는 것은 일반적으로 나쁜 관행으로 간주됩니다.
8진수(Octal)와 16진수(Hexadecimal) 리터럴
- 8진수 리터럴(literal)을 사용하려면 리터럴 앞에 0(숫자 0)을 접두사로 붙여야 합니다.
- int x{ 012 };
- 8진수는 거의 사용되지 않으므로, 사용을 피하는 것을 권장합니다.
- 16진수 리터럴을 사용하려면 리터럴 앞에 0x를 접두사로 붙여야 합니다.
- int x{ 0xF };
2진수 표현에 16진수 사용하기 (Using hexadecimal to represent binary)
- 16진수 한 자리는 16개의 서로 다른 값을 가질 수 있으므로, 16진수 한 바퀴는 4비트(1111)를 포괄한다고 말할 수 있습니다.
- 결과적으로, 16진수 두 자리 쌍을 사용하면 1바이트(8비트)를 정확하게 표현할 수 있습니다.
- 2진수 값 0011 1010 0111 1111 1001 1000 0010 0110을 가진 32비트 정수를 생각해 봅시다.
- 길이와 숫자의 반복 때문에 읽기가 쉽지 않습니다.
- 16진수로는 이 값을 3A7F 9826으로 표현할 수 있으며, 이는 훨씬 간결합니다.
- 이러한 이유로 16진수 값은 메모리 주소나 (데이터 타입을 알 수 없는) 메모리 상의 원시 데이터(raw data)를 표현하는 데 자주 사용됩니다.
2진수 리터럴 (Binary literals)
- C++14 이전에는 2진수 리터럴에 대한 지원이 없었습니다.
- 하지만 16진수 리터럴이 유용한 대안을 제공했습니다.
- C++14부터는 0b 접두사를 사용하여 직접 2진수 리터럴을 사용할 수 있습니다.
자릿수 구분자 (Digit separators)
- 긴 리터럴은 읽기 어려울 수 있기 때문에, C++14에서는 작은따옴표(')를 자릿수 구분자로 사용하는 기능이 추가되었습니다.
- 자릿수 구분자는 순수하게 시각적인 용도이며 리터럴 값에는 아무런 영향을 주지 않습니다.
- int bin { 0b1011'0010 };
- long value { 2'132'673'462 };
10진수, 8진수, 16진수로 값 출력하기
- 기본적으로 C++은 값을 10진수로 출력합니다. 하지만 std::dec, std::oct, std::hex 입출력 조정자(I/O manipulator)를 사용하여 출력 형식을 변경할 수 있습니다.
- std::dec : 10진수
- std::oct : 8진수
- std::hex : 16진수
- 한 번 적용된 입출력 조정자는 다시 변경될 때까지 향후 출력에 계속 적용된다는 점에 유의하세요.
2진수로 값 출력하기
- std::cout에는 2진수 출력 기능이 내장되어 있지 않기 때문에, 2진수로 값을 출력하는 것은 조금 더 어렵습니다.
- 다행히 C++ 표준 라이브러리에는 이를 대신해 줄 std::bitset이라는 타입이 있습니다(bitset 헤더에 포함).
- std::bitset을 사용하려면 std::bitset 변수를 정의하고 저장할 비트 수를 알려주어야 합니다.
- 비트 수는 반드시 컴파일 시간 상수(compile-time constant)여야 합니다.
- “컴파일 시간 상수(compile-time constant)”는 프로그램을 실행하기 전, 즉 컴파일(번역)하는 순간에 값이 이미 확정되어 있는 상수를 의미합니다.
- std::bitset은 정수형 값(10진수, 8진수, 16진수, 2진수 등 모든 형식 포함)으로 초기화할 수 있습니다.
Format / Print 라이브러리를 사용해 2진수 출력하기 (심화)
- C++20과 C++23에서는 새로운 Format 라이브러리(C++20)와 Print 라이브러리(C++23)를 통해 2진수를 출력하는 더 좋은 옵션을 제공합니다.
- #include <format> // C++20
- #include <print> // C++23
최적화 (Optimization) 소개
- 프로그래밍에서 최적화(optimization)는 소프트웨어가 더 효율적으로 작동하도록(예: 더 빠르게 실행되거나 더 적은 리소스를 사용하도록) 수정하는 과정을 말합니다.
- 어떤 종류의 최적화는 일반적으로 수작업으로 이루어집니다.
- 프로파일러(profiler)라고 불리는 프로그램을 사용하여 프로그램의 다양한 부분이 실행되는 데 걸리는 시간을 확인하고, 어떤 부분이 전체 성능에 영향을 미치는지 파악할 수 있습니다.
- 그러면 프로그래머는 이러한 성능 문제를 완화할 방법을 찾을 수 있습니다.
- 수동 최적화는 시간이 오래 걸리기 때문에, 프로그래머들은 보통 큰 영향을 미치는 고수준의 개선(더 성능이 좋은 알고리즘 선택, 데이터 저장 및 접근 방식 최적화, 리소스 사용량 감소, 작업 병렬화 등)에 집중합니다.
- 다른 종류의 최적화는 자동으로 수행될 수 있습니다.
- 다른 프로그램을 최적화하는 프로그램을 최적화기(optimizer, 옵티마이저)라고 합니다.
- 현대 C++ 컴파일러들은 최적화 컴파일러(optimizing compiler)입니다.
- 즉, 컴파일 과정의 일부로 프로그램을 자동으로 최적화할 수 있는 능력이 있습니다.
- 전처리기(preprocessor)와 마찬가지로, 이러한 최적화는 소스 코드 파일을 직접 수정하는 것이 아니라, 컴파일 과정의 일부로서 투명하게 적용됩니다.
as-if 규칙 (The as-if rule)
- C++에서 컴파일러는 프로그램을 최적화할 수 있는 많은 재량권을 가집니다.
- as-if 규칙은 컴파일러가 프로그램의 "관찰 가능한 동작(observable behavior)"에 영향을 주지 않는 한, 더 최적화된 코드를 생성하기 위해 프로그램을 원하는 대로 수정할 수 있다는 규칙입니다.
컴파일 시간 평가 (Compile-time evaluation)
- 현대 C++ 컴파일러는 특정 표현식을 (런타임이 아닌) 컴파일 시간(compile-time)에 완전히 또는 부분적으로 평가할 수 있는 능력이 있습니다.
- 컴파일러가 표현식을 컴파일 시간에 완전히 또는 부분적으로 평가하는 것을 컴파일 시간 평가(compile-time evaluation)라고 합니다.
상수 접기 (Constant folding)
- 상수 접기(Constant folding)는 컴파일러가 리터럴 피연산자를 가진 표현식을 그 결과값으로 대체하는 최적화 기법입니다.
- 상수 접기는 전체 표현식이 런타임에 실행되어야 하는 경우라도, 부분 표현식(subexpression)에 적용될 수 있습니다.
- 위 예제에서 3 + 4는 전체 표현식 std::cout << 3 + 4 << '\n';의 부분 표현식입니다. 컴파일러는 이를 std::cout << 7 << '\n';으로 최적화할 수 있습니다.
상수 전파 (Constant propagation)
- 상수 전파(Constant propagation)는 컴파일러가 상수 값을 갖는 것으로 알려진 변수를 그 값으로 대체하는 최적화 기법입니다.
- 상수 전파는 그 결과를 다시 상수 접기로 최적화할 수 있는 형태로 만들기도 합니다.
죽은 코드 제거 (Dead code elimination)
- 죽은 코드 제거(Dead code elimination)는 실행될 수는 있지만 프로그램의 동작에 아무런 영향을 미치지 않는 코드를 컴파일러가 제거하는 최적화 기법입니다.
- 변수가 더 이상 필요하지 않아 프로그램에서 제거되었을 때, 우리는 그 변수가 최적화로 제거되었다(optimized out 또는 optimized away)고 말합니다.
Const 변수는 최적화하기 더 쉽습니다
- 비상수 변수의 경우 상수 전파 최적화 기법을 적용하기 위해 컴파일러는 비상수 변수의 값이 실제로 변경되지 않았다는 것을 파악해야 합니다.
- 컴파일러가 이를 수행할 수 있는지 여부는 프로그램의 복잡도와 컴파일러 최적화 루틴의 정교함에 달려 있습니다.
- 가능한 한 상수 변수(constant variable)를 사용함으로써 우리는 컴파일러가 더 효과적으로 최적화하도록 도울 수 있습니다.
용어 정리: 컴파일 시간 상수 vs 런타임 상수
- C++에서 상수는 때때로 비공식적인 두 가지 범주로 나뉩니다.
- 컴파일 시간 상수(compile-time constant)
- 런타임 상수(runtime constant)
- 컴파일 시간 상수(compile-time constant)는 컴파일 시간에 그 값을 알 수 있는 상수입니다.
- 리터럴 (Literals)
- 초기화 식(initializer)이 컴파일 시간 상수인 상수 객체
- 런타임 상수(runtime constant)는 런타임 컨텍스트에서 값이 결정되는 상수입니다.
- 상수 함수 매개변수
- 초기화 식이 비상수이거나 런타임 상수인 상수 객체
- 실전에서 이러한 용어들을 접하게 되겠지만, C++에서 이 정의들은 그리 유용하지 않습니다.
- 어떤 런타임 상수(심지어 비상수 변수까지도)는 최적화 목적을 위해 컴파일 시간에 평가될 수 있습니다 (as-if 규칙에 따라).
- 어떤 컴파일 시간 상수는 컴파일 시간 기능(compile-time features)에 사용될 수 없습니다.
상수 표현식 (Constant expressions)
- C++ 표준은 "컴파일 타임"이라는 단어를 거의 언급하지 않습니다.
- 대신 표준은 "상수 표현식(constant expression)"을 정의합니다.
- 이는 반드시 컴파일 타임에 평가 가능해야 하는 표현식을 말합니다.
- 상수 표현식이 아닌 표현식은 종종 비상수 표현식(non-constant expression)이라 불립니다.
- 비공식적으로는 런타임 표현식(runtime expression)이라고도 합니다
가장 일반적으로, 상수 표현식은 다음을 포함합니다.
- 리터럴 (예: 5,1.2)
- 상수 표현식으로 표현 가능한 피연산자를 가진 대부분의 연산자 (예: 3+4, 2*sizeof(int))
- 상수 표현식으로 초기화된 const 정수형 변수 (예: const int x { 5 };). 이는 역사적인 예외이며, 현대 C++에서는 constexpr 변수가 선호됩니다.
- constexpr 변수
- 상수 표현식 인수를 가진 constexpr
다음은 상수 표현식에 사용될 수 없습니다.
- 비상수 변수
- 상수 표현식 초기화식을 가졌더라도, const 비정수형 변수 (예: const double d { 1.2 };)
- constexpr이 아닌 함수의 반환값
- 함수 매개변수 (함수가 constexpr이라도 안 됨).
- 상수 표현식이 아닌 피연산자를 가진 연산자
- 예: x나 y가 상수 표현식이 아닐 때 x + y
- 예: std::cout이 상수 표현식이 아니므로 std::cout << "hello\n"
- 연산자 new, delete, throw, typeid, 그리고 operator, (쉼표 연산자).
- 위의 요소 중 하나라도 포함하는 표현식은 런타임 표현식입니다.
대략적인 요약
- const: “이 *이름(객체)*을 통해 값을 바꾸지 못하게 하겠다” (불변성/읽기 전용)
- 상수 표현식(constant expression): “컴파일 타임에 값이 확정될 수 있는 식”
- constexpr: “이 변수/함수는 상수 표현식으로 쓰일 수 있게(컴파일 타임 평가 가능하게) 만들겠다”
| 구분 | 의미 | 값이 컴파일 타임에 확정? | 대표 용도 |
|---|---|---|---|
| const | 수정 금지(불변) | ❌일 수도 / ✅일 수도 | API 안정성, const-correctness |
| 상수 표현식 | 컴파일 타임에 계산 가능한 “식” | ✅ | 배열 크기, 템플릿 인자, case, static_assert |
| constexpr | 상수 표현식이 될 수 있게 강제/보장 | ✅(변수는 강제) | 컴파일 타임 계산, 성능/안전성, 템플릿 |
컴파일 타임 const의 문제점 (The compile-time const challenge)
- 초기값이 상수 표현식인 정수형 const 변수는 상수 표현식에서 사용할 수 있습니다.
- 그 외의 모든 const 변수는 상수 표현식에서 사용할 수 없습니다.
- 하지만 const를 사용하여 상수 표현식용 변수를 만드는 데는 몇 가지 문제가 있습니다.
- const를 사용하는 것만으로는 해당 변수가 상수 표현식에서 사용 가능한지 즉각적으로 알기 어렵습니다.
const int d { someVar }; // d가 상수 표현식에 사용 가능한지 불분명함
const int e { getValue() }; // e가 상수 표현식에 사용 가능한지 불분명함 - const는 컴파일러에게 "이 변수는 반드시 상수 표현식에서 사용 가능해야 한다"고 알리는(그리고 그렇지 않을 경우 컴파일을 중단시키는) 방법을 제공하지 않습니다.
대신, 조건이 맞지 않으면 조용히 런타임(runtime) 표현식에서만 쓸 수 있는 변수를 생성해 버립니다. - 컴파일 타임 상수 변수를 만들기 위해 const를 사용하는 것은 정수형이 아닌 변수(non-integral variables)에는 적용되지 않습니다. 하지만 정수형이 아닌 변수도 컴파일 타임 상수로 만들고 싶을 때가 많습니다.
constexpr 키워드 (The constexpr keyword)
- constexpr 변수는 항상 컴파일 타임 상수입니다.
- 따라서 constexpr 변수는 반드시 상수 표현식으로 초기화되어야 하며, 그렇지 않으면 컴파일 오류가 발생합니다.
변수에서 const와 constexpr의 의미 비교
- const: 객체의 값이 초기화 이후에 변경될 수 없음을 의미합니다.
- 초기값은 컴파일 타임에 알 수도 있고, 런타임에 알 수도 있습니다.
- const 객체는 런타임에 평가될 수도 있습니다.
- constexpr: 객체가 상수 표현식에서 사용될 수 있음을 의미합니다.
- 초기값은 반드시 컴파일 타임에 알려져야 합니다.
- constexpr 객체는 컴파일 타임 또는 런타임에 평가될 수 있습니다.
- constexpr 변수는 암시적으로 const입니다.
- 반면 const 변수는 암시적으로 constexpr이 아닙니다
- *상수 표현식 초기값을 가진 정수형 const 변수는 제외
- const와 달리 constexpr은 객체의 타입(type)에 포함되지 않습니다.
- 따라서 constexpr int로 정의된 변수의 실제 타입은 const int입니다
- constexpr이 객체에 암시적으로 const를 부여하기 때문입니다.
모범 사례 (Best practice)
- 초기값이 상수 표현식인 모든 상수 변수는 constexpr로 선언하십시오.
- 초기값이 상수 표현식이 아닌(즉, 런타임 상수인) 모든 상수 변수는 const로 선언하십시오.
용어 정리 (Nomenclature recap)
| 용어 (Term) | 정의 (한국어) |
|---|---|
| 컴파일 타임 상수 (Compile-time constant) | 컴파일 타임에 값이 반드시 알려져야 하는 값 또는 수정 불가능한 객체 (예: 리터럴, constexpr 변수). |
| constexpr (Constexpr) | 객체를 컴파일 타임 상수로 선언하는 키워드(그리고 컴파일 타임에 평가될 수 있는 함수를 선언). 비공식적으로는 “상수 표현식(constant expression)”의 약어처럼 쓰이기도 함. |
| 상수 표현식 (Constant expression) | 컴파일 타임 상수와 컴파일 타임 평가를 지원하는 연산자/함수만 포함하는 표현식(즉, 컴파일 타임에 계산 가능한 식). |
| 런타임 표현식 (Runtime expression) | 상수 표현식이 아닌 표현식(즉, 실행 중에 계산되는 식). |
| 런타임 상수 (Runtime constant) | 값이 바뀌지 않는(수정 불가능한) 값/객체이지만, 컴파일 타임 상수는 아닌 것(값이 런타임에 확정되는 상수). |
constexpr 함수 간단 소개 (A brief introduction to constexpr functions)
- constexpr 함수는 상수 표현식 내에서 호출될 수 있는 함수입니다.
- constexpr 함수가 속한 상수 표현식이 반드시 컴파일 타임에 평가되어야 하는 경우 해당 함수는 반드시 컴파일 타임에 평가되어야 합니다.
- 그렇지 않은 경우, constexpr 함수는 컴파일 타임(조건 충족 시) 또는 런타임에 평가될 수 있습니다.
- 컴파일 타임 실행을 위해서는 모든 인자가 상수 표현식이어야 합니다.
- constexpr 함수를 만들기 위해서는 함수 선언 시 반환 타입 앞에 constexpr 키워드를 붙입니다.
std::getline()을 사용하여 텍스트 입력받기
- std::getline()은 두 개의 인자가 필요합니다.
- 첫 번째는 std::cin 입니다.
- 두 번째는 문자열 변수입니다.
#include <iostream>
#include <string> // std::string과 std::getline을 위해
int main()
{
std::cout << "Enter your full name: ";
std::string name{};
std::getline(std::cin >> std::ws, name); // 텍스트 한 줄 전체를 name으로 읽음
std::cout << "Enter your favorite color: ";
std::string color{};
std::getline(std::cin >> std::ws, color); // 텍스트 한 줄 전체를 color로 읽음
std::cout << "Your name is " << name << " and your favorite color is " << color << '\n';
return 0;
}
std::ws란 도대체 무엇인가요?
- C++는 입력이 받아들여지는 방식을 변경하는 입력 조작자(input manipulators) 도 지원합니다.
- std::ws 입력 조작자는 std::cin에게 추출 전에 선행 공백(leading whitespace) 을 무시하도록 지시합니다.
- 선행 공백은 문자열의 시작 부분에 나타나는 모든 공백 문자(스페이스, 탭, 개행)를 말합니다.
- std::ws는 호출 간에 유지되지 않으므로 각 std::getline() 호출마다 수행해야 합니다.
std::string의 길이
- std::string에 문자가 몇 개 있는지 알고 싶다면, std::string 객체에 길이를 물어볼 수 있습니다.
- 이를 수행하는 문법은 이전에 본 것과는 다르지만 꽤 간단합니다.
#include <iostream>
#include <string>
int main()
{
std::string name{ "Alex" };
std::cout << name << " has " << name.length() << " characters\n";
return 0;
}
- 문자열 길이를 length(name)과 같이 묻는 대신 name.length()라고 쓴다는 점을 주목하세요.
- 이것은 std::string 내부에 중첩된 멤버 함수(member function) 라고 불리는 특별한 종류의 함수입니다.
- length() 멤버 함수는 std::string 내부에 선언되어 있기 때문에 문서에서는 때때로 std::string::length()로 표기되기도 합니다.
- std::string::length()는 부호 없는 정수형 값(대부분 size_t 타입)을 반환한다는 점을 유의하세요.
- 길이를 int 변수에 할당하고 싶다면, 부호 있는/없는(signed/unsigned) 변환에 대한 컴파일러 경고를 피하기 위해 static_cast를 해야 합니다:
- int length { static_cast<int>(name.length()) };
- C++20에서는 std::ssize() 함수를 사용하여 std::string의 길이를 크기가 큰 부호 있는 정수형으로 얻을 수 있습니다.
- std::cout << name << " has " << std::ssize(name) << " characters\n";
std::string 초기화는 비용이 듭니다
- std::string이 초기화될 때마다, 초기화에 사용된 문자열의 복사본이 만들어집니다.
- 문자열을 복사하는 것은 비용이 많이 들기 때문에, 복사 횟수를 최소화하도록 주의해야 합니다.
std::string을 값으로 전달하지 마십시오 (Do not pass std::string by value)
- std::string이 함수에 값으로 전달(passed by value)되면, std::string 함수 매개변수는 인자로 인스턴스화되고 초기화되어야 합니다.
- 이는 비용이 많이 드는 복사를 초래합니다.
std::string을 위한 리터럴
- 우리는 큰따옴표 문자열 리터럴 뒤에 s 접미사를 사용하여 std::string 타입의 문자열 리터럴을 만들 수 있습니다.
- s는 반드시 소문자여야 합니다.
std::string_view (C++17)
- std::string의 초기화(또는 복사) 비용이 비싸다는 문제를 해결하기 위해, C++17에서는 std::string_view를 도입했습니다.
- std::string_view는 <string_view> 헤더에 존재 합니다.
- std::string_view는 기존 문자열에 대해 복사본을 만들지 않고 읽기 전용(read-only) 접근을 제공합니다.
- 읽기 전용이란 보고 있는 값을 접근하고 사용할 수는 있지만, 수정할 수는 없다는 뜻입니다.
std::string_view 리터럴 (Literals)
- 이중 인용부호로 감싼 문자열 리터럴은 기본적으로 C 스타일 문자열 리터럴입니다.
- 이중 인용부호 문자열 리터럴 뒤에 sv 접미사(suffix)를 사용하여 std::string_view 타입의 리터럴을 만들 수 있습니다.
- sv는 반드시 소문자여야 합니다.
댓글 0