Не удается вернуть результат сложения строк

Рейтинг: -2Ответов: 1Опубликовано: 10.02.2023

Результат метода успешно записывается в переменную, если верить отладчику, но при возвращении переменной данные теряются и возвращается случайный набор символов.

    MyString& operator + (const MyString& other)
    {
        MyString result;
        result.str = new char[strlen(this->str) + strlen(other.str) + 1];
        
        for (int i = 0; i < strlen(str); i++)
            result.str[i] = this->str[i];

        for (int i = 0; i < strlen(other.str); i++)
            result.str[i+strlen(this->str)] = other.str[i];

        result.str[strlen(str) + strlen(other.str)] = '\0';
        return result;

Уточняю код класса:

class MyString
{
public:
    MyString()
    {
    str = nullptr;
    size = 0;
    }

    MyString(const char* str)
    {
        size_t size = strlen(str);
        this->str = new char[size+1];

        for (int i = 0; i < size; i++)
        {
            this->str[i] = str[i];
        }
        this->str[size] = '\0';
    }

    ~MyString()
    {
        delete[] this->str;
    }

    void Print()
    {
        cout << str;
    }

    MyString(const MyString& other)
    {
        size_t size = strlen(other.str);
        this->str = new char[size + 1];

        for (int i = 0; i < size; i++)
        {
            this->str[i] = other.str[i];
        }

        this->str[size] = '\0';
    };

    MyString& operator = (const MyString& other)
    {
        if (this->str != nullptr)
            delete[] str;

        size_t  size = strlen(other.str);
        this->str = new char[size + 1];

        for (int i = 0; i < size; i++)
        {
            this->str[i] = other.str[i];
        }
        this->str[size] = '\0';

        return *this;
    };

    MyString operator + (const MyString& other)
    {
        MyString result;
        result.str = new char[strlen(this->str) + strlen(other.str) + 1];
        
        for (int i = 0; i < strlen(str); i++)
            result.str[i] = this->str[i];

        for (int i = 0; i < strlen(other.str); i++)
            result.str[i+strlen(this->str)] = other.str[i];

        result.str[strlen(str) + strlen(other.str)] = '\0';
        return result;
    }

private:
    char* str;
    size_t size{};
};

Ответы

▲ 0Принят

Нельзя возвращать из функций ссылки и указатели на временные объекты. При выходе из функции эти объекты удаляются и ссылка/указатель становятся невалидными.

//MyString& operator + ()  // неправильно
//MyString* operator + ()  // неправильно
MyString operator + ()   // правильно
{
    MyString result; // локальная переменная с временем жизни внутри функции
    ....
    return result;
}  // при выходе из функции result уничтожается

Ещё хотелось бы увидеть код самого класса MyString.
Потому что вы выделяете память (ресурс) вне класса. Т.е. работа с памятью не инкапсулирована и скорее всего у вас утечка. Вы не поддерживаете идиому RAII в своем коде.

MyString result;
result.str = new char[strlen(this->str) + strlen(other.str) + 1]; // выделение памяти вручную вне класса
// А освобождение памяти вы сделали?

Сразу бросается в глаза - вы не следуете принципу DRY - Не повторяйся. У вас выделение памяти происходит во многих местах. Для избежания ошибок сделайте служебную функцию и выделяйте/перевыделяйте память в ней, например что-то типа allocate(). И её уже вызывайте в остальных методах.
Ещё одна типичная ошибка при работе с ресурсами - вы не сохраняете валидность объекта до конца операции. Всегда нужно учитывать, что при работе с ресурсами может произойти ошибка. В данном случае - ОС не смогла выделить память. В этом случае, объект который был должен сохраниться либо, перейти в другое, но тоже валидное состояние. Т.е. изначальная строка должна остаться как была, либо например стать пустой строкой.

MyString& operator = (const MyString& other)
{
    if (this->str != nullptr)
        delete[] str;        // освободили память
    this->str = new char[size + 1];  // а вот здесь произошла системная ошибка и память не выделилась
// в деструкторе вы попытаетесь повторно вызвать delete[] str;

// -------------------
// сохранить исходную строку:
{
    char* tmpstr = new char[size + 1];
    if(!tmpstr)
        throw MemoryError(); // если память не выделилась - как-то прореагировать
    
    if (this->str != nullptr)
        delete[] str;        // освободили память
    str = tmpstr;

// -------------------
// строка становится пустой, но это тоже валидное состояние:
MyString& operator = (const MyString& other)
{
    if (this->str != nullptr)
        delete[] str;        // освободили память
    str = nullptr; 
    size = 0;
    this->str = new char[size + 1];
    if(!tmp.str)
        throw MemoryError(); // если память не выделилась - как-то прореагировать

И вот эту всю логику лучше сделать в одном месте - в отдельной функции, чтобы в случае внесения изменений не искать по всему листингу все места, где вы выделяли память.