Я пишу строковый класс для строк UTF-8, и я столкнулся с проблемой проектирования.
Я хотел бы, чтобы некоторые из моих методов const char *
и const Utf8String &
в качестве входных значений. Это сделано для того, чтобы избежать вычисления длины и проверки строки UTF-8, когда пользователь передает Utf8String
и избежать бесполезного выделения памяти для временного Utf8String
когда пользователь передает const char *
,
Я тоже хочу перегрузить const char *
оператор (эквивалентно string::c_str()
) потому что это удобно. Проблема в том, что это создает двусмысленность.
Вот почему строки STL обеспечивают c_str()
метод вместо оператора перегрузки const char *
?
И что я могу сделать здесь? Держите перегруженный оператор const char *
и только одна из двух возможных подписей для моих методов (либо const char *
или же const Utf8String &
) или удалите const char *
перегрузка оператора и сохранение двух возможных сигнатур методов?
Я предлагаю написать два метода вместо использования (const char*)
оператор. Это может привести к различным проблемам.
Вопрос в том, как управляется память возвращаемого указателя?
Можно написать фатальный код, например так:
const char* getText() {
YourType x = "text";
return x;
}
Здесь ваш тип разрушен, и, скорее всего, ваш массив символов. Но это хорошо компилируется и выглядит хорошо. Трудно определить проблему в вашем коде.
Использование выделенного метода позволяет вам сделать использование понятным:
class YourType {
public:
const char* createCharArray() const;
const char* accessCharArray() const;
}
Один метод выделит новый массив символов, в то время как другой просто создаст внутреннее временное представление символов, которое уничтожается самим типом.
Другая проблема заключается в использовании конструктора, подобного этому:
class YourType {
public:
YourType(const char *str);
}
Не совсем понятно, что делает этот конструктор. Ваш класс хранит указатель на строку, или ваш класс создает внутреннюю копию строки. Это, скорее всего, приведет к коду, который может быть трудно понять.
void printText(const YourType &text) {
}
Позволяет это:
printText("Text");
Но также работает для этого:
void foo(const char* text) {
char *str = new char[strlen(text)+1];
std::strcpy(str, text);
printText(str);
}
Здесь я предлагаю использовать static
метод преобразования вместо:
class YourType {
public:
static YourType fromCharArray(const char *str);
}
Написание собственного строкового класса очень весело (и хорошее упражнение). Но я обнаружил, что этот стандарт std::string
отлично работает на струнах UTF-8.
Прежде чем я полностью уйду от темы, я отвечу на ваши вопросы так: когда сомневаетесь, имитируйте стандартные вещи (т.е. std::string
). По крайней мере, две причины, почему это сделать. Первый, эти интерфейсы обычно хорошо разработаны (так как над ними работало много людей с разным опытом). второй, разработчики обычно уже знают, как правильно использовать эти вещи.
Но вернемся к строкам UTF-8. Я не знаю зачем тебе нужна отдельный класс для строк UTF-8 и сколько времени вы потратили на размышления о том, действительно ли вам это нужно (мне интересно узнать причины). Дело в том, что UTF-8 является частью стандарта Unicode, который не так прост. И если вам нужна полнофункциональная строка UTF-8, вы в конечном итоге внедрите приличную часть стандарта Unicode (например, формы нормализации, странные правила использования заглавных букв) (знаете ли вы это по буквам греческого языка Сигма имеет разные правила использования заглавных букв в конце слова?) и т. д.). Я уверен, что вы не хотите этого делать, потому что, если вам это нужно, вероятно, лучше использовать ICU, Qt или какую-то другую библиотеку с достойной поддержкой Unicode.
Итак, если вам нужно просто хранилище для символов Utf-8 с некоторыми utf8_to_encoding()
, utf8_from_encoding()
, utf8_next_code_point()
Я рекомендую придерживаться std::string
и предоставить другие функции, которые вам нужны в качестве бесплатных функций. Например:
std::string utf8_from_utf16(const uint16_t *s, size_t len);
Может быть мой другой ответ на аналогичную тему может быть полезным.