У меня есть фрагмент кода теста C ++, который отлично работает в Rstudio, который использует g ++, но приведет к ошибке во время выполнения в MSVS 2012:
template<typename T>
struct vec{
T*head, *tail;
vec()
{
head=NULL;
tail=NULL;
}
vec(int n)
{
head=(T*)malloc(sizeof(T)*n);
tail=head+n;
}
T operator [] (int i) const {return *(head+i);}
T & operator [] (int i) {return *(head+i);}
~vec(){free(head);}
};
int main(){
std::vector<int>y(3);
y[0]=1; y[1]=2; y[2]=3;
vec<int>x1(3);
x1[0]=y[0];
std::cout<<"vec of integers, [] overloading works fine \n";
std::vector<std::vector<int>::iterator>z(3);
z[0]=y.begin();
z[1]=y.begin()+1;
z[2]=y.begin()+2;
vec<std::vector<int>::iterator>x2(3);
x2[0]=z[0];
std::cout<<*x2[0]<<"\n";
std::cout<<"vec of std::vector::iterator, [] overloading g++ works fine, MSVS doesn't \n";
return 1;
}
Код показывает, что если vec содержит целые числа, MSVS и Rstudio работают нормально; если vec содержит итераторы, MSVS столкнется со следующей ошибкой:
Я чувствовал, что это как-то связано с итераторами. Кто-нибудь может дать мне знать, где моя ошибка? Кстати, я включил только файл заголовка вектора в MSVS.
Спасибо!
Вы выделяете (неинициализированную) память, используя malloc
и пытаться интерпретировать его как не-POD типы, такие как итераторы. Это обязательно потерпит неудачу — operator=
поскольку объект ожидает инициализированный объект в качестве своего левого операнда, но вы задаете ему значение мусора (отсюда и сбой).
Кроме того, вы не вызываете деструкторы ваших элементов, так как вы используете простой free
,
Простое решение заключается в использовании new T[n]
в вашем конструкторе вместо malloc
(а также delete[]
в деструкторе). Это требует вашего T
быть конструируемым по умолчанию; Есть способы ослабить это требование (включая размещение new
и некоторые хитрости, чтобы гарантировать выравнивание).
Кстати, ваш vec
Класс не реализует ни «правило трех», ни «правило пяти», ни отключает конструкцию копирования, поэтому вы получите двойное освобождение в случае, если ваш объект vec будет когда-либо скопирован.
VS имеет дополнительную проверку ошибок, запечатываемую итераторам в режиме отладки. Он нашел законную ошибку в вашем коде! Ошибка в том, что вы повторно интерпретируете неинициализированную память как инициализированные объекты:
vec(int n){head=(T*)malloc(sizeof(T)*n);tail=head+n;}
^^^^^^^^^^^^^^^^^^^^^^^
T & operator [] (int i) {return *(head+i);}
^^^^^^^^^
x2[0]=z[0];
^^^^^^
Назначение звонков operator=
на объекте итератора вектора, который является действительно неинициализированной памятью, что приводит к сбою, который вы видите, когда он пытается интерпретировать часть этой памяти как допустимые данные. (Кстати, 0xCDCDCDCD — это то, чем отладчик обычно заполняет неинициализированную память, чтобы помочь быстрее обнаруживать подобные ошибки.)
Я предлагаю использовать new
/delete
вместо malloc. Простое выделение памяти сложно сделать правильно в C ++ (где нормальное управление памятью уже достаточно сложное).
Обратите внимание, что в общем случае malloc
может не дать блоков с достаточно высоким выравниванием для всех типов объектов (хотя это будет происходить в большинстве случаев, так как очень немногие объекты выровнены по более чем 16 байтам).