Valgrind обнаружил 3 утечки памяти, но я не могу понять, где они - PullRequest
2 голосов
/ 31 мая 2019

Я пытался реализовать базовый класс String самостоятельно, и он работает нормально, но Вэлгринд говорит, что есть 3 утечки памяти, и я не могу понять, где и почему.Я действительно пытался удалить все после того, как он больше не используется (я начал использовать Valgrind сегодня).Теперь я действительно обеспокоен своими базовыми знаниями C / C ++ по управлению памятью.Я сделал комментарий к местам в коде, где Valgrind обнаружил утечку (// VALGRIND).Я также загрузил снимок экрана с этим сообщением об ошибке нажмите, чтобы увидеть снимок экрана .

РЕДАКТИРОВАТЬ: Я обновил снимок экрана, чтобы вы могли видеть полный вывод.

StringT.h

template<typename char_type = char>
class StringT {
public:

 explicit StringT(const char_type *str) {
        if (str != nullptr) {
            size_t len = strlen(str);
            m_str = new char_type[len + 1]; //VALGRIND: 6 bytes in 1 blocks are definitely lost in loss record 1 of 3
            strcpy(m_str, str);
        }
    }

   ~StringT() {
        delete [] m_str;
    }


StringT(const StringT & other) {
        size_t len = 0;
        if (other.m_str) len = strlen(other.m_str);
        m_str = new char_type[len + 1]; //VALGRIND: 6 bytes in 1 blocks are definitely lost in loss record 2 of 3
        strcpy(m_str, other.m_str);
    }

    StringT(StringT && other) noexcept {
        m_str = other.m_str;
        other.m_str = nullptr;
    }


     StringT & operator+=(const StringT &other) {
        if (other.m_str == nullptr) //when other str is empty just return current Str
            return *this;

        const size_t mysize{m_str ? strlen(m_str) : 0}; // check if not null then call strlen
        const size_t osize{other.m_str ? strlen(other.m_str) : 0};

        char *newStr = new char_type[osize + mysize + 1]; //VALGRIND: 11 bytes in 1 blocks are definitely lost in loss record 3 of 3
        newStr[0] = '\0'; //strcat searches for '\0', so newStr has to be a valid String

        if (m_str) strcat(newStr, m_str);
        if (other.m_str) strcat(newStr, other.m_str);

        delete[] m_str; //delete old string
        m_str = newStr; //set member to new concatenated str

        return *this;
    }

    size_t length() const {
        if (!m_str) return 0;
        return strlen(m_str);
    }


    friend
    std::ostream &operator<<(std::ostream &out, StringT<> &other) {
        if (other.m_str) out << other.m_str;
        return out;
    }

private:
    char_type *m_str{nullptr};
};

main.cpp

int main() {

    const char *cArr = "Hello";
    const char *cArr2 = "World";
    StringT<char> hello(cArr);
    StringT<char> world(cArr2);
    StringT<char> emptyStr;

    std::cout << "hello: " << hello << std::endl;
    std::cout << "world: " << world << std::endl;
    std::cout << "emptyStr: " << emptyStr << std::endl;

    StringT<char> hCopy(hello);
    StringT<char> wMove(std::move(world));

    std::cout << "hCopy: " << hello << std::endl;
    std::cout << "hCopy: " << hCopy << std::endl;
    std::cout << "world: " << world << std::endl;
    std::cout<<  "wMove: " << wMove << std::endl;
    std::cout<<  "lenMove: " << wMove.length() << std::endl;
    std::cout<<  "lenEmptyStr: " << emptyStr.length() << std::endl;

    hello += wMove;
    std::cout<<  "hello += world: " << hello << std::endl;

    return 0;
}

Ответы [ 2 ]

4 голосов
/ 31 мая 2019

Ваше удаление здесь:

StringT() {
    delete [] m_str;
}

Но это конструктор, а не деструктор.?

1 голос
/ 31 мая 2019

Как уже ответили, delete должен быть в деструкторе.Тем не менее, правильное решение вашей проблемы - не делать управление памятью вручную в этом случае.Вы должны использовать std::unique_ptr для своего m_str члена:

std::unique_ptr<char_type[]> m_str;

Это освобождает вас от необходимости вручную new и delete.Это также помогает защитить от утечек памяти в случае исключений.Даже если вы delete выделите все, что у вас есть, у вас все равно могут возникнуть утечки памяти, если между new и delete возникнет исключение.unique_ptr помогает предотвратить утечку такого типа.

Вашему классу нужны только небольшие изменения:

template<typename char_type = char>
class StringT {
public:
    StringT()
    {}

    explicit StringT(const char_type *str)
    {
        if (str != nullptr) {
            size_t len = strlen(str);
            m_str = std::make_unique<char_type[]>(len + 1);
            strcpy(m_str.get(), str);
        }
    }

    StringT(const StringT & other)
    {
        size_t len = 0;
        if (other.m_str)
            len = strlen(other.m_str.get());
        m_str = std::make_unique<char_type[]>(len + 1);
        strcpy(m_str.get(), other.m_str.get());
    }

    StringT(StringT && other) noexcept
    {
        m_str = std::move(other.m_str);
    }

    StringT & operator+=(const StringT &other)
    {
        if (other.m_str == nullptr)
            return *this;

        const size_t mysize{m_str ? strlen(m_str.get()) : 0};
        const size_t osize{strlen(other.m_str.get())};

        auto newStr = std::make_unique<char_type[]>(osize + mysize + 1);
        newStr[0] = '\0';

        if (m_str)
            strcat(newStr.get(), m_str.get());
        strcat(newStr.get(), other.m_str.get());

        m_str = std::move(newStr);
        return *this;
    }

    size_t length() const
    {
        if (!m_str)
            return 0;
        return strlen(m_str.get());
    }

    friend
    std::ostream &operator<<(std::ostream &out, StringT<> &other)
    {
        if (other.m_str)
            out << other.m_str.get();
        return out;
    }

private:
    std::unique_ptr<char_type[]> m_str;
};

Вы заметите, что в new или delete нет вызововэтот кодm_str автоматически удалит выделенную память при необходимости.

...