Я пишу библиотеку UTF-8 для C++ как осуществление, поскольку это - мой первый реальный код C++. До сих пор я реализовал конкатенацию, символьную индексацию, парсинг и кодирование UTF-8 в классе, названном "ustring". Похоже, что это работает, но два на вид эквивалентных способа объявить новый ustring ведут себя по-другому. Первый путь:
ustring a;
a = "test";
работы и перегруженное "=" оператор анализирует строку в класс (который хранит строки Unicode как динамично выделенный международный указатель). Однако следующее не работает:
ustring a = "test";
потому что я получаю следующую ошибку:
test.cpp:4: error: conversion from ‘const char [5]’ to non-scalar type ‘ustring’ requested
Существует ли путь к обходному решению эта ошибка? Это, вероятно - проблема с моим кодом, все же. Следующее - то, что я записал до сих пор для библиотеки:
#include <cstdlib>
#include <cstring>
class ustring {
int * values;
long len;
public:
long length() {
return len;
}
ustring * operator=(ustring input) {
len = input.len;
values = (int *) malloc(sizeof(int) * len);
for (long i = 0; i < len; i++)
values[i] = input.values[i];
return this;
}
ustring * operator=(char input[]) {
len = sizeof(input);
values = (int *) malloc(0);
long s = 0; // s = number of parsed chars
int a, b, c, d, contNeed = 0, cont = 0;
for (long i = 0; i < sizeof(input); i++)
if (input[i] < 0x80) { // ASCII, direct copy (00-7f)
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = input[i];
} else if (input[i] < 0xc0) { // this is a continuation (80-bf)
if (cont == contNeed) { // no need for continuation, use U+fffd
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = 0xfffd;
}
cont = cont + 1;
values[s - 1] = values[s - 1] | ((input[i] & 0x3f) << ((contNeed - cont) * 6));
if (cont == contNeed) cont = contNeed = 0;
} else if (input[i] < 0xc2) { // invalid byte, use U+fffd (c0-c1)
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = 0xfffd;
} else if (input[i] < 0xe0) { // start of 2-byte sequence (c2-df)
contNeed = 1;
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = (input[i] & 0x1f) << 6;
} else if (input[i] < 0xf0) { // start of 3-byte sequence (e0-ef)
contNeed = 2;
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = (input[i] & 0x0f) << 12;
} else if (input[i] < 0xf5) { // start of 4-byte sequence (f0-f4)
contNeed = 3;
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = (input[i] & 0x07) << 18;
} else { // restricted or invalid (f5-ff)
values = (int *) realloc(values, sizeof(int) * ++s);
values[s - 1] = 0xfffd;
}
return this;
}
ustring operator+(ustring input) {
ustring result;
result.len = len + input.len;
result.values = (int *) malloc(sizeof(int) * result.len);
for (long i = 0; i < len; i++)
result.values[i] = values[i];
for (long i = 0; i < input.len; i++)
result.values[i + len] = input.values[i];
return result;
}
ustring operator[](long index) {
ustring result;
result.len = 1;
result.values = (int *) malloc(sizeof(int));
result.values[0] = values[index];
return result;
}
char * encode() {
char * r = (char *) malloc(0);
long s = 0;
for (long i = 0; i < len; i++) {
if (values[i] < 0x80)
r = (char *) realloc(r, s + 1),
r[s + 0] = char(values[i]),
s += 1;
else if (values[i] < 0x800)
r = (char *) realloc(r, s + 2),
r[s + 0] = char(values[i] >> 6 | 0x60),
r[s + 1] = char(values[i] & 0x3f | 0x80),
s += 2;
else if (values[i] < 0x10000)
r = (char *) realloc(r, s + 3),
r[s + 0] = char(values[i] >> 12 | 0xe0),
r[s + 1] = char(values[i] >> 6 & 0x3f | 0x80),
r[s + 2] = char(values[i] & 0x3f | 0x80),
s += 3;
else
r = (char *) realloc(r, s + 4),
r[s + 0] = char(values[i] >> 18 | 0xf0),
r[s + 1] = char(values[i] >> 12 & 0x3f | 0x80),
r[s + 2] = char(values[i] >> 6 & 0x3f | 0x80),
r[s + 3] = char(values[i] & 0x3f | 0x80),
s += 4;
}
return r;
}
};
Ваша проблема в том, что ustring a = "test"
фактически вызывает конструктор, а не оператор присваивания. ура, добро пожаловать в c++ :)
Вам нужно определить себе как конструктор по умолчанию, так и тот, который принимает const char*
, потому что как только вы определите a конструктор, вам нужно будет определить все ваши конструкторы.
Несколько других вещей:
const char *
вместо char[]
(вы не изменяете входные данные и char* более распространен)sizeof
делает не то, что вы думаете, он не работает правильно для параметров массива. Она возвращает вам sizeof(char*)
, а не sizeof(array)
. this
из ваших операторов. vector values;
, чтобы управлять всей вашей памятью за вас. encode()
, вероятно, должна возвращать string
. С string
освобождать
или удалять
ее. s.append(c);
вместо realloc
. printf("%s", s.c_str());
, но в c++ обычно используется cout << s;
Например, так:
class ustring {
public:
// Default constructor, allows you to create your class with no arguments.
ustring() { ...; }
// Allows you to create your class from string literals.
ustring(const char *input) { ...; }
// Copy constructor, allows you to create your class from other instances.
ustring(const ustring &input) { ...; }
// Assignment operators.
ustring &operator=(const ustring &input) { ...; return *this; }
ustring &operator=(const char *input) { ...; return *this; }
};
int main() {
ustring s, t; // invokes default constructor.
s = t; // invokes ustring assignment op.
s = "test"; // invokes const char* assignment op.
ustring u = "test"; // invokes const char* constructor.
ustring v("test"); // invokes const char* constructor.
ustring x(u); // invokes copy constructor.
}
Если это c++, зачем вы делаете все эти malloc/realloc? Я не полностью разобрал этот код, но я предполагаю, что есть более простой способ... см. комментарий об использовании вектора.
Как отметил в комментариях @Michael Aaron Safyan, если вы выделяете память для класса ustring
, вы захотите деаллоцировать ее в деструкторе. Однако я думаю, что, перейдя на контейнеры с управлением памятью - vector & string - вы избежите любого собственного управления памятью и сможете избежать написания деструктора.
Это две операции:
ustring a; // construct a new object using constructor
a = "test"; // assign value to object using operator=
Это одна операция:
ustring a = "test"; // construct with a value, aka value-intialization
В интересах эффективности времени выполнения и обеспечения семантической свободы C ++ не экстраполирует конструктор по умолчанию ustring :: ustring ()
и оператор присваивания ustring :: operator = (const char *)
в конструктор ustring :: ustring (const char *)
.
Но для большинства разумных строковых классов это будет работать:
ustring::ustring(const char *str)
: /* initialize ustring::ustring() does */ {
/* do whatever ustring::ustring() does */
*this = str; // assign value.
}
Лучше вызвать оператор присваивания из конструктора, чем пытаться сделать наоборот.
Конечно, вы, вероятно, можете повысить эффективность, учитывая длину данной строки при выполнении инициализации.