lundi 5 juillet 2021

Error std::bad_array_new_length in a template

I was trying to define my own class template Array<T> to practice the usage of templates.
The code I produced builds properly, but when executed it gives the following error

terminate called after throwing an instance of 'std::bad_array_new_length'
  what():  std::bad_array_new_length

I think I have found a solution to the problem, but I would be interested to see if there was an underlying error in the previous code and if so, which one.

This is the code I previously wrote:

#include <iostream>

class Empty{
private:
    char error;
public:
    Empty(char e) : error(e) { std::cout << "Azione non disponibile, lista vuota" << std::endl;}
};

template <class T>
class Array;

template <class T>
std::ostream& operator<<(std::ostream&,const Array<T>&);

template <class T>
class Array{
    friend std::ostream& operator<< <T> (std::ostream&,const Array<T>&);
private:
    T* arr;
    unsigned int size;
    unsigned int capacity;

    static T* copia(T* a, unsigned int s, unsigned int c){
        if(c > 0) {
            T* app = new T[c];
            for (int i = 0; i<s; ++i) {
                app[i] = a[i];
            }
            return app;
        }else return nullptr;
    }

public:
    Array(int k = 0, const T& t = T()) : size(k > 0 ? k : 0), capacity(size), arr(k > 0 ? new T[size] : nullptr){
        for (int i = 0; i < k ; ++i) arr[i] = t;
    }

    Array(const Array& a) : size(a.size), capacity(a.capacity), arr(copia(a.arr,a.size,a.capacity)){}

    Array& operator=(const Array& a){
        if(this != &a){
            delete[] arr;
            capacity = size = a.size;
            arr = copia(a.arr,a.size,a.capacity);
        }
        return *this;
    }

    ~Array(){delete[] arr;}

    void pushBack(const T& t) {
        if(size == capacity){
            capacity > 0 ? capacity *= 2 : capacity = 1;
            T* app = copia(arr,size, capacity);
            delete[] arr;
            arr = app;
        }
        ++size;
        arr[size-1] = t;
    }

    T popBack() {
        if (size != 0) {
            T temp = arr[size - 1];
            --size;
            return temp;
        } else throw Empty('e');
    }

};

template <class T>
std::ostream& operator<<(std::ostream& os ,const Array<T>& a){
    for (int i = 0; i < a.size; ++i) {
        os << a.arr[i] << ' ';
    }
    std::cout << std::endl;
    return os;
}

int main(){

    Array<int> a(5,5),e;
    std::cout << a << std::endl;

    a.pushBack(16);
    a.pushBack(17);
    a.pushBack(18);

    std::cout << a << std::endl;

    return 0;
}

If I run this code without the a.pushBack(x) function call, it works.
As soon as I insert even one function call, I get that error in the output.

While debugging, I realized that the line where I had written T* arr was not the correct one.
Knowing that the constructor follows the order of initialization of its own sub-objects, the first element to be constructed is the pointer. Since I'm trying to create a vector of elements of T with dimension size, rightly gives me the error, as I have not yet initialized the integer size.
So I solved it by swapping the lines.

template <class T>
class Array{
    friend std::ostream& operator<< <T> (std::ostream&,const Array<T>&);
private:
    unsigned int size;
    unsigned int capacity;
    T* arr;
    ...
};

But at this point I wonder: why, if I don't make the function call, I don't get the same error, knowing that size at the time of construction is undefined?

Logically, the problem should also occur in that case, but everything seems to work:

enter image description here

PS: Don't count on the fact that I didn't handle the exception being thrown, the code is not yet fully complete, but for the moment I was keen to at least implement the Rule of Three.

Aucun commentaire:

Enregistrer un commentaire