Произошла ошибка Обнаружено повреждение кучи C ++ - PullRequest
2 голосов
/ 01 мая 2020

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

enter image description here

И мой полный код здесь.

#include <iostream>
using namespace std;

class String {
public :
    String() { 
        strData = NULL; 
        len = 0;
        cout << "constructor executed !" << endl;
    }

    String(const char *str){
        len = strlen(str);
        strData = new char[len+1];
        strcpy_s(strData, sizeof(str), str);

        cout << sizeof(str) << endl;
        cout << sizeof(len) << endl;
        cout << sizeof(strData) << endl;
        cout << strData << endl;
    }
    ~String() {
        delete[] strData; //Fails here!
    }
    char* GetStrData() const{ 
        return strData;
    }
    int GetLen() const {
        return len;
    }
private : 
    char* strData; 
    int len;
};

int main() {
    String str; 
    str = String("Hi");
}

и результат здесь

enter image description here

Я предположил, что это сообщение из-за размера strData, но я точно не знаю.

Мне нужна помощь от вас, ребята. спасибо.

Ответы [ 3 ]

1 голос
/ 01 мая 2020

Эта строка:

str = String("Hi");

(очень приблизительно) эквивалентна:

String tmp("hi")
str.strData = tmp.strData;
str.len = tmp.len;

Так что, когда оба значения tmp и str получаются удалено, вы в итоге дважды вызываете delete по одному и тому же адресу.

Вам нужно написать собственный метод operator=() в вашем классе String, чтобы справиться с этим.

После того, как вы откроете ящик этого пандоры, вы должны пересечь свой Ц и правильно расставить точки. Это известно как правило 0/3/5. См. этот вопрос для деталей:

1 голос
/ 01 мая 2020

Краткий ответ:

Оператор str = String("Hi"); вызывает стандартный оператор copy-assignment (неявно объявленный) специальной функции-члена вашего класса. При уничтожении (в вашей программе создаются два объекта) одна и та же память будет уничтожена дважды, поскольку два объекта в вашей программе указывают на одну и ту же память, и оба они считают себя владельцем памяти.

Длинный ответ:

Поскольку вы еще не определили оператор копирования, и сгенерированная компилятором версия будет копировать указатели (shallow copy) на другой класс.

Если для типа класса (структура, класс или объединение) не заданы пользовательские операторы назначения копирования, компилятор всегда объявляет их как встроенный общедоступный c член класса.

Объявленный имплицитом оператор присваивания копии, сгенерированный компилятором, будет выглядеть следующим образом:

String& operator=(const String& other)
{
    strData = other.strData;
    len = other.len;
    return *this;
}

Итак, неявно объявленная функция-член оператора копирования присваивает мелкую копию и следовательно, он просто копирует указатели (а не их значение). До конца оператора str = String("Hi"); у вас будет два String объекта (str объект и временный объект, созданный String("Hi")), и их strData будут указывать на один и тот же массив символов. Когда временный файл, сгенерированный String("Hi"), будет уничтожен, он освободит память, а когда объект str будет уничтожен, он также попытается освободить / удалить ту же самую память и, следовательно, вы получите heap поврежденным.

Подробнее о копирование и замена идиома. Попробуйте это:

String& operator=(String other)
{
    swap(other, *this);
    return *this;
}

void swap(String& first, String& second)
{
    std::swap(first.strData, second.strData);
    std::swap(first.len, second.len);
}

Попробуйте программу ниже. Я прокомментировал оператор copy-assignment, чтобы продемонстрировать уничтожение указателя дважды.

#include <iostream>

#include <string.h>

class String {
public :
    String(void): strData(nullptr), len(0)
    { 
        std::cout << "constructor executed !" << std::endl;
    }

    String(const char *str)
    {

        len = strlen(str);
        strData = new char[len + 1];
        std::copy(str, str + len + 1, strData);
        std::cout << "Constructing memory: " << strData << std::endl;
        std::cout << sizeof(str) << std::endl;
        std::cout << sizeof(len) << std::endl;
        std::cout << sizeof(strData) << std::endl;
        std::cout << strData << std::endl;
    }

 //    String& operator=(String other)
    // {
    //  swap(other, *this);
    //  return *this;
    // }

    ~String()
    {
        if (strData == nullptr)
            return;

        std::cout << "Destroying memory: " << (void *)strData << std::endl;
        delete[] strData; //Fails here!
    }

    char* GetStrData(void) const
    { 
        return strData;
    }

    int GetLen(void) const
    {
        return len;
    }

    void swap(String& first, String& second)
    {
        std::swap(first.strData, second.strData);
        std::swap(first.len, second.len);
    }

private : 
    char* strData; 
    int len;
};

int main() 
{
    String str; 
    str = String("Hi");
    return 0;
}

Вывод:

constructor executed !
Constructing memory: Hi
8
4
8
Hi
Destroying memory: 0x7fffc5628080
Destroying memory: 0x7fffc5628080
0 голосов
/ 01 мая 2020

В вашем конструкторе строка strcpy_s(strData, sizeof(str), str); является проблемной c. Предполагается, что вторым параметром будет размер целевого буфера len + 1, который вы использовали в операторе new. Помните, sizeof(strData) - это размер указателя, это всегда 8 байтов для 64-битных машин или 4 байта для 32-битных машин. Это не длина строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...