Реализовать оператор [][]

Всем добрый день. Не могу понять, почему мой код не проходит тесты на 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 шт):

Автор решения: Ariox

У вас изначально не совсем верная идея. Это задача на паттерн 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 или аналог для результата, т.к. для работы с подстрокой не нужна ее полная копия, достаточно указателя на начало и размера.

→ Ссылка
Автор решения: AR Hovsepyan

Известный мне строковой класс:

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;
}
→ Ссылка