SGI STL 참조 사본에 캐릭터 특성에 대한 페이지가 있지만 이것이 어떻게 사용되는지 볼 수 없습니다. string.h 함수를 대체합니까? 에 의해 사용되지 않는 것 같습니다 std::string
. 예를 들어의 length()
메서드 std::string
는 Character Traits length()
메서드를 사용하지 않습니다 . 캐릭터 특성이 존재하고 실제로 사용되는 이유는 무엇입니까?
답변:
문자 특성은 스트림 / 문자열 클래스 가 저장 되는 문자 의 논리와 해당 문자에 대해 수행해야하는 조작 의 논리 를 분리 할 수 있도록하므로 스트림 및 문자열 라이브러리의 매우 중요한 구성 요소입니다 .
우선 기본 문자 특성 클래스 인는 char_traits<T>
C ++ 표준에서 광범위하게 사용됩니다. 예를 들어라는 클래스가 없습니다 std::string
. 오히려 std::basic_string
다음과 같은 클래스 템플릿 이 있습니다.
template <typename charT, typename traits = char_traits<charT> >
class basic_string;
그런 다음 다음과 std::string
같이 정의됩니다.
typedef basic_string<char> string;
마찬가지로 표준 스트림은 다음과 같이 정의됩니다.
template <typename charT, typename traits = char_traits<charT> >
class basic_istream;
typedef basic_istream<char> istream;
그렇다면 이러한 클래스가있는 그대로 구조화 된 이유는 무엇입니까? 왜 이상한 특성 클래스를 템플릿 인수로 사용해야합니까?
그 이유는 어떤 경우에는와 똑같은 문자열을 원할 수 std::string
있지만 약간 다른 속성 이 있기 때문입니다 . 이에 대한 전형적인 예는 대소 문자를 무시하는 방식으로 문자열을 저장하려는 경우입니다. 예를 들어, 다음 CaseInsensitiveString
과 같은 문자열을 만들 수 있습니다.
CaseInsensitiveString c1 = "HI!", c2 = "hi!";
if (c1 == c2) { // Always true
cout << "Strings are equal." << endl;
}
즉, 대소 문자 구분 만 다른 두 개의 문자열이 동일하게 비교되는 문자열을 가질 수 있습니다.
이제 표준 라이브러리 작성자가 특성을 사용하지 않고 문자열을 설계했다고 가정합니다. 이것은 내 상황에서 완전히 쓸모없는 엄청나게 강력한 문자열 클래스를 표준 라이브러리에 가지고 있음을 의미합니다. 비교는 항상 내가 원하는 방식에 대해 작동하기 때문에이 문자열 클래스에 대해 많은 코드를 재사용 할 수 없습니다. 그러나 트레이 트를 사용하면 실제로 std::string
대소 문자를 구분하지 않는 문자열을 얻기 위해 구동하는 코드를 재사용 할 수 있습니다.
C ++ ISO 표준의 복사본을 가져 와서 문자열의 비교 연산자가 작동하는 방식에 대한 정의를 살펴보면 compare
함수 측면에서 모두 정의되어 있음을 알 수 있습니다. 이 함수는 다음을 호출하여 정의됩니다.
traits::compare(this->data(), str.data(), rlen)
str
비교할 문자열은 어디에 rlen
있고 두 문자열 길이 중 더 작은 문자열입니다. 이것은의 정의가 템플릿 매개 변수로 지정된 특성 유형에 의해 내 보낸 함수 를 compare
직접 사용 한다는 것을 의미하기 때문에 실제로 매우 흥미 롭습니다 compare
! 결과적으로 새로운 특성 클래스를 정의한 다음 compare
대소 문자를 구분하지 않고 문자를 비교하도록 정의 하면와 같이 동작 std::string
하지만 대소 문자를 구분하지 않는 처리 하는 문자열 클래스를 만들 수 있습니다 !
여기에 예가 있습니다. std::char_traits<char>
작성하지 않은 모든 함수에 대한 기본 동작을 얻기 위해 상속합니다 .
class CaseInsensitiveTraits: public std::char_traits<char> {
public:
static bool lt (char one, char two) {
return std::tolower(one) < std::tolower(two);
}
static bool eq (char one, char two) {
return std::tolower(one) == std::tolower(two);
}
static int compare (const char* one, const char* two, size_t length) {
for (size_t i = 0; i < length; ++i) {
if (lt(one[i], two[i])) return -1;
if (lt(two[i], one[i])) return +1;
}
return 0;
}
};
( 동등 및보다 작음의 문자를 각각 비교 한 다음 이 함수의 관점에서 정의하는 eq
및 lt
여기도 정의 compare
했습니다.)
이제이 특성 클래스가 있으므로 CaseInsensitiveString
간단하게 다음과 같이 정의 할 수 있습니다.
typedef std::basic_string<char, CaseInsensitiveTraits> CaseInsensitiveString;
그리고 짜잔! 이제 모든 것을 대소 문자를 구분하지 않는 문자열이 생겼습니다!
물론이 외에도 특성을 사용하는 다른 이유가 있습니다. 예를 들어 고정 크기의 기본 문자 유형을 사용하는 문자열을 정의하려는 경우 char_traits
해당 유형을 전문화 한 다음 해당 유형에서 문자열을 만들 수 있습니다. 예를 들어 Windows API에는 TCHAR
전처리 중에 설정 한 매크로에 따라 좁거나 넓은 문자 유형 이 있습니다. 그런 다음 다음과 같이 TCHAR
작성 하여 s에서 문자열을 만들 수 있습니다.
typedef basic_string<TCHAR> tstring;
이제 TCHAR
s 문자열이 있습니다.
이 모든 예제에서 해당 유형에 대한 문자열을 가져 오기 위해 일부 특성 클래스 (또는 이미 존재하는 것을 사용)를 일부 템플릿 유형에 대한 매개 변수로 정의했음을 주목하십시오. 이것의 요점은 basic_string
저자가 특성을 사용하는 방법을 지정하기 만하면 기본 문자열 유형의 일부가 아닌 약간의 뉘앙스 또는 특이한 문자열을 얻기 위해 기본이 아닌 특성을 사용하도록 마술처럼 만들 수 있다는 것입니다.
도움이 되었기를 바랍니다!
편집 : @phooji가 지적 했듯이이 특성 개념은 STL에서만 사용되는 것이 아니며 C ++에만 국한되지 않습니다. 완전히 뻔뻔한 자기 프로모션으로, 얼마 전 특성을 사용하여 모든 유형의 문자열을 저장하고 클라이언트가 저장하기 원하는 비교 유형을 사용 하는 삼항 검색 트리 ( 여기에 설명 된 기수 트리 유형)의 구현을 작성 했습니다 . 이것이 실제로 사용되는 예를보고 싶다면 흥미로운 읽을 수 있습니다.
편집 : std::string
사용하지 않는 귀하의 주장에 대한 응답으로 traits::length
몇 군데에서 수행되는 것으로 나타났습니다. 당신이 만들 때 가장 두드러진 std::string
중 char*
C 스타일의 문자열을 문자열의 새 길이를 호출하여 파생 된 traits::length
해당 문자열에. 그 보인다 traits::length
반면, C ++에서 문자열의 "최소 공통 분모"문자가의 C 스타일의 시퀀스에 대처하기 위해 주로 사용되는 std::string
임의의 내용의 문자열과 함께 작업하는 데 사용됩니다.