2014-10-21 6 views
2

Недавно я столкнулся с странной ошибкой при кодировании классов с вектором в качестве члена. Ошибка была введена мной, но результат выполнения меня шокировал. Ниже приведен упрощенный пример.уничтожение объекта с векторным членом

В основном ошибка в конструкторе, где h2 имеет только размер dimension_, а не dimension_*dimension_, что приводит к вне диапазона доступа h2 в void A::set() и double A::get(). Я признаю, что мне плохо, что такая ошибка. Я бы подумал, что это должно произойти, так это то, что h[2] и h[3] в void A::set() будут иметь доступ к некоторому случайному адресу памяти или только в зарезервированном диапазоне векторов h2. Но то, что на самом деле произошло, когда деструктор называется ошибкой free(), возникает ошибка, которая сопровождается множеством фиктивных обратных линий, которые я бы предпочел не перечислять здесь.

*** glibc detected *** ./a.out: free(): invalid next size (fast): 0x0000000001637040 *** 

Но если я не имеют доступа к h[3] в void A::set(), этого не произойдет. Мой вопрос - это то, что действительно происходит с вектором и деструктором? Разве деструктор вектора знает, к каким элементам я обращался? Я думаю, что вектор знает только его размер и освобождает память при вызове деструктора. Любая идея будет оценена по достоинству. Благодарю.

Ниже приведен пример кода, который отображает ошибку времени выполнения.

#include<vector> 
#include<cstddef> 
#include<iostream> 

class A 
{ 
    public: 
     A(size_t dimension); 
     virtual ~A(); 
    public: 
     virtual double get(); 
     virtual void set(); 
    protected: 
     const size_t dimension_; 
     std::vector<double> h1, h2; 
}; 

A::A(size_t dimension): 
    dimension_(dimension), 
    h1(dimension_*dimension_), 
    h2(dimension) 
{} 

A::~A() 
{ 
} 

double A::get() 
{ 
    set(); 
    double result(0), temp(0); 
    for(size_t i(0); i < dimension_; ++i) 
    { 
     temp = 0; 
     for(size_t j(0); j < dimension_; ++j) 
      { 
       temp += h1[j] * h2[j + i*dimension_]; 
      } 
     result += temp * h1[i]; 
    } 
    return result; 
} 

void A::set() 
{ 
    h1[0] = h1[1] = h1[2] = h1[3] = 0.5; 
    h2[0] = h2[1] = h2[2] = h2[3] = 0.005; 
} 

int main() 
{ 
    A mya(2); 
    std::cout << mya.get() << "\n"; 
    return 0; 
} 

ответ

3

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

Обратите внимание, что методпроведет проверку границ и выдаст исключение, если вы попытаетесь получить доступ к элементу вне пределов. Если вы получаете доступ к элементу «за пределами границ», в тот момент вы получите право на исключение, что упростит отладку. Недостатком является то, что проверки границ занимают немного дополнительного времени, поэтому, если вы делаете миллионы векторных обращений, вы можете заметить, что at() немного медленнее, чем operator[]. Независимо от того, стоит ли компромисс, зависит от вас.


Сказав, что, вероятно, происходит то, что вы затирание некоторых структур бухгалтерского учета, используемых в вашем C++ кучного распределителя выполнению. Распределители кучи обычно выделяют немного больше памяти, чем запрошено, и используют дополнительное пространство для хранения данных о самом распределении.

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

+0

Я не думал, что UB может быть настолько глубоким. Я должен сказать, что эта ошибка действительно дала мне некоторое время в отладке. Спасибо, что указали метод 'at()'. Я знал этот метод, но я также знаю, что для проверки границ есть оценка производительности, поэтому для вектора, размер которого известен мне, я предпочитаю 'operator []'. – Devin

+0

Спасибо за ваш ответ, который имеет для меня полный смысл. Я думаю, что я должен использовать 'at()' при отладке. – Devin

+1

Обратите внимание, что большинство реализаций 'std :: vector' будут проверять границы, если вы скомпилируете его правильно: по умолчанию используется MSC, с g ++, за исключением оптимизированных построений, вы должны использовать' -D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC'.Когда у вас есть ошибка границ, вы не делаете _not_ хотите выбросить исключение. –

Смежные вопросы