Программирование на C ++: получение ошибки или нет в зависимости от используемого компьютера. - PullRequest
0 голосов
/ 29 июня 2018

Привет всем,

Вот код в программировании на c ++:

#include <iostream>

using namespace std;

class Buffer {

    double* doubles;
    int size;

    public:

    Buffer(int size): size(size), doubles(new double[size]) 
    {
        for (int i=0; i<size; ++i) doubles[i] = 0.0;
    }

    ~Buffer() {
        delete[] doubles;
    }

    void fill(double d) {
        Buffer newBuffer(size);
        for (int i=0; i<size; ++i) newBuffer.doubles [i] = d;
        this->doubles = newBuffer.doubles;
    }

    void print() const {
        for (int i=0; i<size; ++i) cout << doubles[i] << " ";
        cout << endl;
    }

};

int main() {
     Buffer b1(5); b1.print();
     Buffer b2(5); b2.fill(12.34); b2.print();
}`

Этот код не должен выполняться без исключения. Действительно, когда мой учитель запускает его (со своим компьютером), он получает следующую ошибку: * Ошибка в a.out: двойное освобождение или повреждение (fasttop): 0x0000000001178c90 *. Тем не менее, когда я запускаю его (на своем компьютере), я не получаю никаких ошибок, и код выполняется без каких-либо исключений.

Я публикую скриншот моего терминала после запуска кода: Снимок экрана терминала.

Как вы можете видеть на скриншоте, для запуска кода я использую компилятор clang (мой учитель тоже) с версиями c ++ 11, 14 и 17. Таким образом, моя проблема не является проблемой версии C ++. Добавив тот факт, что я использую Linux поверх Ubuntu, и моего учителя тоже, это, вероятно, не проблема операционной системы.

Мне очень интересно узнать, в чем причина такого результата?

Заранее спасибо.

Ответы [ 6 ]

0 голосов
/ 29 июня 2018

Вы можете переопределить оператор присваивания, чтобы сделать глубокое копирование. Это решит вашу проблему.

0 голосов
/ 29 июня 2018

Ваша функция fill приводит к утечке памяти, что в свою очередь вызывает UB. Вы можете испытывать странное поведение, сбой, отсутствие симптомов или проблемы в других программах из-за нарушения памяти. Точное наблюдение зависит от платформы и шанса.

0 голосов
/ 29 июня 2018

Ваша программа представляет собой то, что известно как неопределенное поведение (по очевидным причинам, указанным в других ответах), когда поведение во время выполнения непредсказуемо .

Следовательно, результат при запуске кода на разных машинах, с разными компиляторами, в разное время, разными людьми ... вполне может быть совершенно разным с аварийным завершением ( double free или коррупция ) будучи наиболее доброкачественным (в отличие от бега без явной ошибки).

Конечно, неопределенного поведения следует избегать как чумы. К сожалению, это сложнее сказать, чем сделать, так как нет надежных инструментов, чтобы обнаружить все это. Многие программисты даже не знают, что представляет собой неопределенное поведение, а что нет.

0 голосов
/ 29 июня 2018

Недостаток в fill. Вы создаете новый Buffer объект с именем newBuffer, а затем удаляете doubles в this (что фактически вызывает утечка памяти ). newBuffer удалит свой буфер, когда он выйдет из области видимости в конце fill, и деструктор вызовет main, когда b2 выйдет из области видимости!

Это неопределенное поведение : delete[] вызывается дважды по одному и тому же указателю. Boom .

Лучшее, что нужно сделать на милю страны, - это использовать std::vector<double> в качестве ученика. Наличие голых указателей в качестве учеников - вот путь к неприятностям.

0 голосов
/ 29 июня 2018

Проблема в этих двух строках:

Buffer newBuffer(size);
...
this->doubles = newBuffer.doubles;

Первая строка создает новый объект Buffer. Во второй строке this->double указывает на ту же память, на которую указывает newBuffer.doubles. Таким образом, у вас есть два отдельных указателя оба указателя на одну и ту же память .

Теперь, когда это сделано, объект newBuffer выходит из области видимости и разрушается. И деструктор освободит память, на которую указывают this->doubles и newBuffer.doubles. Эта память больше не доступна для вашего приложения, ее использование каким-либо образом приводит к неопределенному поведению . Это включает в себя как печать , так и , более конкретно, удаление памяти за секунду времени в деструкторе.

Самый простой способ решить вашу проблему - это понять, что вам не нужен временный newBuffer объект, вы можете просто инициализировать this->doubles[i] прямо в цикле:

for (int i=0; i<size; ++i) this->doubles [i] = d;

Что касается того, почему иногда кажется, что это работает, то это потому, что одно из возможных поведений UB ( Undefined Behavior ), по-видимому, работает нормально. В других случаях это может привести к сбою или другому странному поведению в, казалось бы, не связанных частях вашей программы.

0 голосов
/ 29 июня 2018

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

Вы создаете новый буфер с новым массивом, заполняете его некоторыми значениями, а затем переназначаете указатель вашего старого буфера (потеря следа некоторой памяти - утечка памяти). После этого новый буфер выходит из области видимости, поэтому вызывается деструктор, удаляющий память как старого, так и нового буфера. И теперь вы вызываете печать на память, которая не принадлежит вам, следовательно, вы портите кучу. И затем, вдобавок ко всему, вы пытаетесь освободить его снова, даже если он уже был освобожден.

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

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