我最近在使用vector作为成员编写类时遇到了一个奇怪的错误。这个bug是由我自己引入的,但运行时结果让我感到震惊。简化示例如下所示。
基本上,错误位于构造函数中,其中h2
的大小只有dimension_
而不是dimension_*dimension_
,这会导致h2
的{{1}}超出范围void A::set()
和double A::get()
。我承认有这样的错误是我的坏事。我认为应该发生的是h[2]
中的h[3]
和void A::set()
将访问一些随机内存地址或仅在矢量h2
的保留范围内。但真正发生的事情是,当析构函数被调用时,会引发free()
错误,其后跟着很多虚拟的Backtrace,我宁愿不在这里列出。
*** 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;
}
答案 0 :(得分:3)
使用超出范围的参数调用向量的operator[]
会导致未定义的行为(UB),因此技术上任何事情都可能发生,包括您案件中发生的事情。一旦你调用UB,所有的赌注都会被取消;你无法提前知道调用UB的代码的影响。
请注意,at()
方法将执行边界检查,如果您尝试访问越界元素,则会抛出异常。如果你确实访问了一个越界元素,那么你将在那一刻得到一个异常,这使调试变得更容易。缺点是边界检查需要一些额外的时间,因此如果您进行数百万次向量访问,您可能会注意到at()
比operator[]
慢一点。是否值得权衡取决于你。
话虽如此,可能发生的事情是你正在破坏C ++实现的堆分配器使用的一些簿记结构。堆分配器通常会分配比请求的内存更多的内存,并使用额外的空间来存储有关分配本身的数据。
当向量去释放用于保存向量中元素的分配时,分配器发现它的簿记数据已被破坏,因此你最终得到了这个断言。