Я не могу понять где допустил ошибки при работе с памятью

такое дело,я не понимаю из-за чего появляются следующие ошибки:если в конструкторе копирования убрать вывод cout << this->index<<"W"; то после перестаёт работать ostream& operator<<(ostream &os, const CMass& a) при попытке вывода d(14 стр в main.cpp); Ещё при создании f(15 стр в main.cpp) вызывается функция setSize и когда я ввожу значение, выдаёт нарушение прав доступа при чтении по адресу. И в конце при выходе из программы выдаёт ошибку :/ Я не понимаю где накосячил.

main.cpp

#include "mass.h";
using namespace std;
int main()
{
    cout << fixed;
    srand(time(0));
    setlocale(LC_ALL, "rus");
    try
    {
        CMass <double>a(10, 7);
        CMass <double>b(10, 5);
        CMass <double>d = a;
        cout << d;
        CMass <double>f;
        cout << f;
        cout << "a = " << a;
        cout << "b = " << b;
        CMass <double>e(10, 1);
        e = b - a;
        cout << "b - a = " << e;
        cout << "b + a = " << b + a;
        cout << "d = " << d;
        cout << b;
        a -= b;
        cout << a;
        cout << a[16];
        double* c = a.Find(1.3);
        if (!c)
            throw "Такого числа нет";
        cout << *c;
        a.Sort(1);
        a.Sort(0);
    }
    catch (const char* ex)
    {
        cout << ex;
    }
    //cin.get();
    return 0;
}

mass.h

#pragma once
#include <iostream>
#include <iomanip>
#include<cmath>
#ifndef MASS_H
#define MASS_H
using namespace std;
//Если не введён размер
void setSize(int&);
template <typename T>
class CMass
{


private:
    int size;
    T* mass;
    int index;
public:
    //Конструктор
    CMass(int s = 0, int I = 0) 
    {
        size = s;
        while (size == 0)
            setSize(size);
        index = I;
        mass = (T*)calloc(size,sizeof(T));
        mass -= index;
        initrand();
    }
    //Конструктор копирования
    CMass(const CMass &  other) 
    {
        cout << this->index<<"W";
        this->index = other.index;
        this->size = other.size;
        this->mass = new T[this->size];
        for (int i = 0; i < size; i++)
            this->mass[i+index] = other.mass[other.index + i];
        cout << this->index<<"W";
    }
    //Поиск элемента
    T* Find(T x) 
    {
        for (int i = index; i < size + index; i++)
            if (mass[i] == x)
                return &mass[i];
        return NULL;
    }
    //Сортировка элементов
    void Sort(bool i)
    {

        if (i)//Если равен 1, тогда по возрастанию
        {
            for (int j = index; j < size + index; j++)
            {
                for (int h = j + 1; h < size + index; h++)
                    if (mass[j] > mass[h])
                    {
                        T tmp;
                        tmp = mass[j];
                        mass[j] = mass[h];
                        mass[h] = tmp;
                    }
            }
        }
        else//Иначе по убыванию
            for (int j = index; j < size + index; j++)
            {
                for (int h = j + 1; h < size + index; h++)
                    if (mass[j] < mass[h])
                    {
                        T tmp;
                        tmp = mass[j];
                        mass[j] = mass[h];
                        mass[h] = tmp;
                    }
            }
    }
    //Генерация чисел
    void initrand() 
    {
        for (int i = index; i < size + index; i++) 
            mass[i] = (T)(round((rand() * (100. + 100) / RAND_MAX - 100)*10)/10);//округление,чтобы был удобен поиск элемента
    }
    //Индексирование
    T& operator [](int CIndex)
    {
        if (CIndex<index || CIndex>index + size-1)
            throw "Вы вышли за границы массива!";
        return mass[CIndex];
    }
    //Вычитание и сложение
    CMass<T> operator+(const CMass<T>& other)
    {
        CMass result(size,0);
        if (size != other.size)
            throw "Размерность массивов разная";
        for (int i = 0; i < result.size; i++)
            result.mass[result.index + i] = mass[index + i] + other.mass[other.index + i];
        return result;
    }
    CMass<T> operator-(const CMass<T>& other)
    {
        CMass result(size, 0);
        if (size != other.size)
            throw "Размерность массивов разная";
        for (int i = 0; i < result.size; i++)
            result.mass[result.index + i] = mass[index + i] - other.mass[other.index + i];
        return result;
    }
    CMass<T> operator+=(const CMass<T>& other)
    {
        return *this + other;
    }
    CMass<T> operator-=(const CMass<T>& other)
    {
        return *this - other;
    }
    friend  void setSize(int& size);
    friend ostream& operator<<(ostream &os, const CMass<T>& a)
    {
        for (int i = 0; i < a.size; i++)
            os << setprecision(1) << a.mass[a.index + i] << " ";
        return os << endl;
    }
};
#endif

mass.cpp

#include "mass.h"
#include <cstring>
#include <iostream>
void setSize(int& size)
{
    cout << "\nВведите размер массива: ";
    cin >> size;
}


Ответы (1 шт):

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

Есть такое правило трех/правило пяти - если есть конструктор копирования, то должен быть и оператор копирования и вести они себя должны "идентично" ( а также деструктор).

Если в классе есть "голый, не прикрытый смартпоинтером" указатель, нужно обязательно их написать.

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

Смотрим дальше. Вы используете то calloc, то new. Так нельзя, нужно что то одно для одной переменной. Да, деструктора у Вас нет, поэтому эту ошибку не сразу словите.

Ещё дальше. Посмотрим а этот кусочек кода

this->mass = new T[this->size];
for (int i = 0; i < size; i++)
    this->mass[i+index] = other.mass[other.index + i];

Очевидно, что размер mass равен size. Дальше идет цикл от 0 до size-1. А внутри цикла используется еще +index. Если index > 0, то гарантирован выход за пределы массива. И дальше этот паттерн повторяется много-много раз.

Я бы вместо указателя и размера использовал бы обычный std::vector. Много кода существенно упростится.

→ Ссылка