【发布时间】:2018-05-22 10:06:39
【问题描述】:
所以我对 C++ 比较陌生,最近遇到了std::allocator 类。我知道这是用于创建vectors、lists、deques 等的强大工具,我正在尝试了解更多有关它的信息。
让我感到困惑的一件事是:
例如,如果我们定义一些allocator<int> 表示为alloc,并使用它通过auto const b = a.allocate(n) 在内存中分配n 位置,其中b 是指向第一个int 元素的指针那么,在分配的内存中,也必须构造分配的内存才能实际访问它,对吧?
如果我们引入一些迭代指针auto e=b,则可以通过alloc.construct(e++,int_obj) 执行构造,其中int_obj 是一些用户初始化的int 类型的对象。只要对construct 的调用总数少于n,这一切都很好。但是,我不太确定当用户调用construct 的次数超过n 时会发生什么。我最初期望一些警告或错误消息会抬起它丑陋的脑袋,但是什么也没发生。作为一个简单的例子,这里是我尝试运行的代码片段:
int n{ 0 }; // Size of the array is initialized to 0.
cin >> n; // User reads in the size.
allocator<int> alloc; // 'alloc' is an object that can allocate ints.
auto const b = alloc.allocate(n); // Pointer to the beginning of the array.
auto e = b; // Moving iterator that will point to the end of the array
for (int i = 0;i != 10;++i)
alloc.construct(e++, i); // We start constructing 10 elements in the array, regardless of the size n, which can in principle be less than 10.
for (auto i = b;i != e;++i)
cout << *i << "\t";
最初我为n=1 运行此代码,一切正常;它打印出从 0 到 9 的数字,即使我只为一位数字分配空间。那已经是一个危险信号了,对吧?然后我把n改成2,打印完第4个数字后程序就中断了,这和我预期的差不多。
我从这种行为中得出的结论是,试图通过std::allocator 构造尚未分配的内存是未定义且不可预测的,因此应该避免这种情况(这是显而易见的)。但是,这似乎是一个非常危险的陷阱,我想知道 C++ 中是否有一些内置的解决方法,在尝试构造未分配的内存时总是会阻止用户。
【问题讨论】:
-
C++ 不是内存安全语言。有很多方法可以产生 未定义的行为 - 这可能会崩溃,或者可能会做一些意想不到的事情,或者可能会按预期“工作” - 这是其中之一。对于 n=1 它似乎“起作用”的事实是巧合。一般来说,如果可能,最好避免手动分配。特别是,您应该很少需要直接使用
allocator;在这种情况下,new int[n]可以很好地完成这项工作。但是,很多人会说你也应该避免使用“原始”数组...... -
@davmac :我知道可以动态分配数组,但是,作为练习,我尝试模拟简单向量类的工作方式。 Vector 类有一个很好的限制,它最初会为后续元素分配一些内存,并且每当将新元素添加到 vector 时,将创建 vector 中的新对象以保存该元素。但是,有些情况我们想添加新元素,但是之前分配的空间已经满了,所以vector需要知道分配更多的内存。它在用户不知情的情况下这样做。这个原理我想明白。
-
在这种情况下,它会分配另一个(更大的)数组,将元素从原始数组复制/移动到新数组中,并释放旧数组。分配一定数量的存储然后使用比分配的更多的存储永远是不正确的(即使它并不总是导致分段错误,也不能期望它以任何可靠的方式运行)。
-
是的,在不存在的内存上构造对象是一个危险的陷阱。程序员有责任通过使用干净的技术来确保不会发生这种情况。一个常见的指导方针是避免使用
new表达式对指针进行任何直接分配/初始化(例如,不要使用some_X = new X),因为很容易误用此类指针(因此访问未分配的内存)。相反,如果您需要对象集合,请使用标准库 - 特别是标准容器(vector、list、string等),或者使用shared_ptr来保存任何new表达式的结果。跨度>