Mat*_*ith 76 c++ string stl stdstring char-traits
我注意到在我的SGI STL参考文献中,有一个关于角色特征的页面,但我看不出这些是如何使用的?他们是否替换了string.h函数?它们似乎没有被使用std::string,例如,length()方法on std::string不使用Character Traits length()方法.为什么角色特征存在并且它们是否曾在实践中使用过?
tem*_*def 160
字符特征是流和字符串库中非常重要的组件,因为它们允许流/字符串类将存储的字符的逻辑与应对这些字符执行的操作的逻辑分开.
首先,默认字符traits类char_traits<T>在C++标准中广泛使用.例如,没有名为的类std::string.相反,有一个类模板std::basic_string,如下所示:
template <typename charT, typename traits = char_traits<charT> >
class basic_string;
Run Code Online (Sandbox Code Playgroud)
然后,std::string定义为
typedef basic_string<char> string;
Run Code Online (Sandbox Code Playgroud)
同样,标准流定义为
template <typename charT, typename traits = char_traits<charT> >
class basic_istream;
typedef basic_istream<char> istream;
Run Code Online (Sandbox Code Playgroud)
那么为什么这些类的结构如此呢?我们为什么要使用奇怪的traits类作为模板参数?
原因是在某些情况下我们可能希望有一个字符串std::string,但有一些略有不同的属性.一个典型的例子是,如果你想以一种忽略大小写的方式存储字符串.例如,我可能想要创建一个CaseInsensitiveString我可以拥有的字符串
CaseInsensitiveString c1 = "HI!", c2 = "hi!";
if (c1 == c2) { // Always true
cout << "Strings are equal." << endl;
}
Run Code Online (Sandbox Code Playgroud)
也就是说,我可以有一个字符串,其中只有两个字符串区别于它们的区分大小写才相等.
现在,假设标准库作者设计的字符串不使用特征.这意味着我在标准库中有一个非常强大的字符串类,在我的情况下完全没用.我无法重用此字符串类的大部分代码,因为比较总是会影响我希望它们如何工作.但是通过使用traits,实际上可以重用驱动的代码std::string来获取不区分大小写的字符串.
如果您提取C++ ISO标准的副本并查看字符串的比较运算符如何工作的定义,您将看到它们都是根据compare函数定义的.该函数又通过调用来定义
traits::compare(this->data(), str.data(), rlen)
Run Code Online (Sandbox Code Playgroud)
str你要比较的字符串在哪里,是rlen两个字符串长度中较小的一个.这实际上非常有趣,因为它意味着定义compare直接使用compare由指定为模板参数的traits类型导出的函数!因此,如果我们定义一个新的traits类,然后定义,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;
}
};
Run Code Online (Sandbox Code Playgroud)
(请注意,我还定义eq和lt在这里,这对分别平等和小于,比较字符,然后定义compare这个功能而言).
现在我们有了这个traits类,我们可以CaseInsensitiveString简单地定义为
typedef std::basic_string<char, CaseInsensitiveTraits> CaseInsensitiveString;
Run Code Online (Sandbox Code Playgroud)
瞧!我们现在有一个字符串,对所有内容都不区分大小写!
当然,除此之外还有其他原因可以使用特征.例如,如果要定义使用固定大小的某些基础字符类型的字符串,则可以专门char_traits处理该类型,然后从该类型创建字符串.例如,在Windows API中,TCHAR根据您在预处理期间设置的宏,有一种类型是窄字符或宽字符.然后,您可以TCHAR通过写入来创建字符串
typedef basic_string<TCHAR> tstring;
Run Code Online (Sandbox Code Playgroud)
现在你有了一串TCHARs.
在所有这些示例中,请注意我们刚刚定义了一些traits类(或使用已存在的类)作为某个模板类型的参数,以便获取该类型的字符串.这一点的全部意义在于basic_string作者只需要指定如何使用特征,我们神奇地可以使它们使用我们的特征而不是默认值来获取具有一些细微差别或怪癖而不属于默认字符串类型的字符串.
希望这可以帮助!
编辑:正如@phooji所指出的,这个特征的概念不仅仅是由STL使用,也不是特定于C++.作为一个完全无耻的自我推销,不久前我写了一个三元搜索树(这里描述的一种基数树)的实现,它使用特征来存储任何类型的字符串,并使用客户希望它们存储的任何比较类型.如果你想看一个在实践中使用它的例子,这可能是一个有趣的读物.
编辑:为了回应你std::string没有使用的声明traits::length,事实证明它在几个地方都有.最值得注意的是,当你构建std::string出的char*C风格的字符串,字符串的新长度是通过调用导出traits::length该字符串.它似乎traits::length主要用于处理C风格的字符序列,它们是C++中字符串的"最小公分母",同时std::string用于处理任意内容的字符串.