Реализовать оператор [][]
Всем добрый день. Не могу понять, почему мой код не проходит тесты на stepik.org, хотя локально работает. Буду рад советам.
В этой задаче вам требуется реализовать оператор [] для уже известного вам класса String. Однако на этот раз оператор должен реализовывать нестандартное поведение: оператор нужно реализовать таким образом, чтобы для объекта str класса String можно было писать str[i][j] и это выражение возвращало подстроку начинающуюся в позиции i (считая с 0) и заканчивающуюся в позиции j (не включая).
String const hello("hello"); String const hell = hello[0][4]; //теперь в hell хранится подстрока "hell" String const ell = hello[1][4]; // теперь в ell хранится подстрока "ell"
Обратите внимание, что i может равняться j, в этом случае результатом должна быть пустая строка. Гарантируется, что i никогда не будет больше j, и они не будут выходить за пределы длины строки.
struct String {
String(const char *str = "");
String(size_t n, char c);
~String();
String(const String &other);
String &operator=(const String &other);
void append(const String &other);
struct SubString {
SubString(const char* str = "", const int index= 0) :size(0), tmp_String(nullptr)
{
size_old = strlen(str);
size = strlen(str + index);
SubString::str = new char[size + 1];
sprintf(SubString::str, "%s", str + index);
}
~SubString()
{
delete[] str;
delete [] str_tmp;
if ( tmp_String)
delete tmp_String;
}
String operator[] (const int index) {
size_t new_len = index - ( size_old - size);
str_tmp = new char[new_len+1];
memcpy(str_tmp, str, new_len);
str_tmp[new_len] = '\0';
tmp_String = new String(str_tmp);
return *tmp_String;
}
private:
size_t size, size_old;
char* str, *str_tmp;
String * tmp_String;
};
SubString operator[] (const int index) const {
return SubString(str, index);
}
size_t size;
char *str;
};
Насколько я вижу у себя под MVS, все деструкторы вызываются.
Ответы (2 шт):
У вас изначально не совсем верная идея. Это задача на паттерн proxy, т.е. нужно создать класс, который содержит ссылку на исходную строку и индекс начала подстроки, и перегрузить у него operator[].
Использование new для новой строки тоже избыточно, т.к. сам массив символов уже лежит в динамической памяти. Если боитесь лишнего глубокого копирования, перегрузите оператор перемещения и погуглите оптимизацию возвращаемый значения.
Должно получиться что-то подобное:
class String{
size_t size;
const char *data;
public:
// Конструировать лучше из указателя и длины строки, чтобы удобно было брать подстроку
String(const char* chars, size_t size); // Глубокое копирование
String(const String& other); // Глубокое копирование
String(String&& other); // Перемещение, нужно просто скопировать указатель и размер, и обнулить указатель в other
class StringRangeProxy{
const String& src;
size_t begin;
public:
StringRangeProxy(const String& src_, size_t begin_)
: src(src_)
, begin(begin_)
{ }
StringRangeProxy(const StringRangeProxy&) = default;
String operator[](size_t end) const{
return String(src.data + begin, size_t(end - begin));
}
};
StringRangeProxy operator[](size_t begin) const{
return StringRangeProxy (*this, begin);
}
};
Еще лучше использовать std::string_view или аналог для результата, т.к. для работы с подстрокой не нужна ее полная копия, достаточно указателя на начало и размера.
Известный мне строковой класс:
using String = std::string;
Теперь мой класс будет просто его наследником, чтобы не повторять код, и будет иметь тип помощник для своего оператора:
class My_String : public String {
struct proxy {
const String& s;
proxy(const String& str) : s(str) {}
String::difference_type start{};
String operator[](string::difference_type off)
{
if (off < start) off = start;
return s.substr(start, off - start);
}
};
public:
My_String(const char* str) : String(str) {}
//по необходимости добавьте другие конструкторы
//а оператор вернет помощника
proxy operator [](const string::difference_type off) const
{
proxy p(*this);
p.start = off;
return p;
}
};
int main() {
My_String str("test program");
cout << str[5][12] << ' ' << str[0][5]; //вывод: program test
return 0;
}

