Почему возникает это исключение bad_alloc?

Я совершенно новичок в C ++ и хотел попробовать создать простой общий (не уверен, что это правильная терминология) класс массива, который по существу работал бы как массивы в java (в основном для удобного поля длины). Вот весь код:

#include <iostream>


template <typename T>
class Array
{
private:
    T* ar;
public:
    const unsigned int length;

    Array(unsigned int length_) : length(length_), ar(new T[length]) {}
    void insert(unsigned int, T);
    T& get(unsigned int);
    T& operator[](unsigned int);
};

template <typename T> void Array<T>::insert(unsigned int index, T dataToAdd)
{
    if(index>=length)
        {throw -1;}
    ar[index]=dataToAdd;
}

template <typename T> T& Array<T>::get(unsigned int index)
{
    if(index>=length)
        {throw -1;}
    return ar[index];
}

template <typename T> T& Array<T>::operator[](unsigned int index)
    {return this->get(index);}


int main()
{
    std::cout << "main start\n";

    Array<int> nums1=Array<int>(2);
    nums1[0]=4;
    nums1[1]=10;
    std::cout << "length of nums1:" << nums1.length << "\n";    

    Array<int> nums2=Array<int>(2);
    nums2[0]=8;
    nums2[1]=5;
    std::cout << "length of nums2:" << nums2.length << "\n";

    Array<int> nums3=Array<int>(2);
    std::cout << "nums3 created\n";
    nums2[0]=3;
    std::cout << "added to index 0\n";
    nums2[1]=15;
    std::cout << "added to index 1\n";
    std::cout << "length of nums3:" << nums3.length <<"\n";

    std::cout << "main end\n";
}

Он отлично компилируется (с использованием MinGW), но когда я запускаю его, я получаю следующий вывод:

main start
length of nums1:2
length of nums2:2
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Поскольку последнее, что печатает cout, это длина nums2:2, я предполагаю, что исключение выдается в строке после: Array<int> nums3=Array<int>(2);

Что является причиной этого? Я не могу быть вне памяти, не так ли?


person EFTH    schedule 13.06.2014    source источник
comment
Изменение конструктора таким образом сработало для меня: Array(unsigned int length_) : length(length_), ar(new T[length_]) {}   -  person dragosht    schedule 13.06.2014
comment
Поскольку вы новичок: Пожалуйста, никогда не используйте слова просто отлично или работает отлично. Если бы эти слова применялись, вам не нужно было бы публиковать сообщения.   -  person Kerrek SB    schedule 13.06.2014
comment
Я думаю, вам нужно узнать о стандартной библиотеке и обнаружить, что этот класс вам не пригодится. Кроме как средство для изучения языка.   -  person David Heffernan    schedule 13.06.2014
comment
У меня тоже сработало, спасибо! Но почему это сработало?   -  person EFTH    schedule 13.06.2014
comment
В дополнение к ответу помните, что в C++ вы должны освободить память, выделенную с помощью new. Итак, вашему классу нужен деструктор, такой как ~Array() { delete[] ar; }   -  person anumi    schedule 13.06.2014
comment
@DavidHeffernan Я знаю, что вектор существует, это просто для изучения языка.   -  person EFTH    schedule 13.06.2014
comment
Если бы вы включили предупреждения для своего компилятора, вы также получили бы сообщение об этом порядке инициализации. Что-то вроде этого warning: Array<int>::length will be initialized after [-Wreorder]   -  person dragosht    schedule 13.06.2014
comment
Это ближайший возможный дубликат, который я могу быстро найти. Легкость даже делает все возможное, чтобы сослаться на стандарт.   -  person WhozCraig    schedule 13.06.2014


Ответы (1)


Независимо от порядка в строке инициализации, переменные-члены инициализируются в том порядке, в котором они объявлены в классе. Таким образом, это:

private:
    T* ar;
public:
    const unsigned int length;

означает, что ar будет инициализирован до length, поэтому так:

Array(unsigned int length_) : length(length_), ar(new T[length]) {}

использует length до его создания. Либо измените порядок описания класса переменных-членов, либо используйте length_ в конструкции ar.

person WhozCraig    schedule 13.06.2014
comment
Конструктор может использовать length просто отлично. - person Rapptz; 13.06.2014
comment
@Rapptz, как написано, нет, не может. Его значение неопределенно. Если порядок в классе decl переключается (как я сказал в своем ответе), тогда все в порядке, так как тогда он будет инициализирован до ar. - person WhozCraig; 13.06.2014
comment
Конструктор может ссылаться на length и ожидать четко определенного значения, если length был инициализирован. Разговор о конструкторе спорный, поскольку проблема заключается в списке инициализаторов. - person David Heffernan; 13.06.2014
comment
@DavidHeffernan для этого должны быть десятки обманов, если я смогу найти один, я брошу это и вместо этого свяжу его. Не знал, сколько времени у ОП было, чтобы получить ответ. - person WhozCraig; 13.06.2014
comment
Ну, так много для того, чтобы бросить это. Я не могу отказаться от принятого ответа (по крайней мере, у меня нет на это смелости). Тем не менее, я связал дубликат, который я думаю лучшие совпадения. Надеюсь, будущие читатели поймут это. - person WhozCraig; 13.06.2014