В чем смысл черт характера STL?
Я замечаю, что в моей копии ссылки SGI STL есть страница о чертах характера, но я не вижу, как они используются? Они заменяют строку.функции h? Они, кажется, не используются std::string
, например,length()
метод on std::string
не использует черты характера length()
метод. Почему существуют черты характера и используются ли они на практике?
1 ответ:
черты характера являются чрезвычайно важным компонентом библиотек потоков и строк, поскольку они позволяют классам stream / string отделять логику какие символы хранятся логика какие манипуляции следует выполнять с этими символами.
для начала, класс черт характера по умолчанию,
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; }
то есть у меня может быть строка, где две строки, отличающиеся только чувствительностью к регистру, сравниваются равными.
теперь предположим,что авторы стандартной библиотеки разработали строки без использования признаков. Это означало бы, что в стандартной библиотеке у меня будет очень мощный класс string это было совершенно бесполезно в моей ситуации. Я не мог повторно использовать большую часть кода для этого класса string, так как сравнения всегда будут работать против того, как я хотел, чтобы они работали. Но с помощью черт, это на самом деле можно повторно использовать код, который управляет
std::string
чтобы получить строку без учета регистра.если вы вытащите копию стандарта ISO C++ и посмотрите на определение того, как работают операторы сравнения строк, вы увидите, что все они определены в терминах
compare
функция. Эта функция в свою очередь определяется вызовомtraits::compare(this->data(), str.data(), rlen)
здесь
str
это строка, которую вы сравниваете с andrlen
является меньшим из двух длин строк. Это на самом деле довольно интересно, потому что это означает, что определение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
автор просто должен указать, как использовать черты характера и мы волшебным образом можно заставить их использовать наши черты, а не по умолчанию, чтобы получить строки, которые имеют некоторые нюансы или причуды не являются частью типа строки по умолчанию.надеюсь, что это помогает!
EDIT: как указал @phooji, это понятие признаков не просто используется STL, но и не является специфичным для C++. Как совершенно бесстыдная самореклама, некоторое время назад я написал реализация троичного дерева поиска (тип корня дерева описано здесь), который использует признаки для хранения строк любого типа и использует любой тип сравнения, который клиент хочет, чтобы они хранились. Это может быть интересно прочитать, если вы хотите увидеть пример того, где это используется на практике.
EDIT: в ответ на ваше утверждение, что
std::string
не использоватьtraits::length
, оказывается, что это делает в нескольких местах. Особенно, когда вы строитеstd::string
Сchar*
строка в стиле C, новая длина строки получается путем звонюtraits::length
на эту строку. Кажется, чтоtraits::length
используется в основном для работы с последовательностями символов в стиле C, которые являются "наименее общим знаменателем" строк в C++, в то время какstd::string
используется для работы со строками произвольной содержание.