【问题标题】:std::allocator<T>: Is constructing on unallocated memory in C++ allowed? [duplicate]std::allocator<T>:是否允许在 C++ 中构建未分配的内存? [复制]
【发布时间】:2018-05-22 10:06:39
【问题描述】:

所以我对 C++ 比较陌生,最近遇到了std::allocator 类。我知道这是用于创建vectors、lists、deques 等的强大工具,我正在尝试了解更多有关它的信息。 让我感到困惑的一件事是:

例如,如果我们定义一些allocator&lt;int&gt; 表示为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),因为很容易误用此类指针(因此访问未分配的内存)。相反,如果您需要对象集合,请使用标准库 - 特别是标准容器(vectorliststring 等),或者使用shared_ptr 来保存任何new 表达式的结果。跨度>

标签: c++ allocator


【解决方案1】:

但是,这似乎是一个极其危险的陷阱,

确实如此,避免这种陷阱是您的责任。这就是为什么始终建议首先使用现有容器的原因,因为它们已经过仔细编写、审查和测试以避免此类错误。

如果您确实需要直接处理原始内存,通常最好编写自己的容器,您可以根据类似的标准对其进行测试,而不是将手动内存管理与程序逻辑的其余部分交错。

我想知道 C++ 中是否有一些内置的解决方法,在尝试构造未分配的内存时总是会阻止用户

不,因为这必然会导致编写时不正确的内容产生运行时成本。我不希望我的程序运行得更慢,因为其他人他们的工作做得不好。

但是,有很多工具可以帮助您测试和诊断这些错误,例如:

  • clang 和 gcc 编译器具有地址清理程序,可将这些检查编译到您的程序中(这是一个可选的编译器工具,而不是语言的一部分)
  • valgrind 是一个外部程序,其默认工具 (memcheck) 运行您的真实程序以查找这些错误。它很慢,但它用于测试或调试,而不是用于实时程序

【讨论】:

    猜你喜欢
    • 2018-06-01
    • 2018-06-13
    • 2016-08-19
    • 1970-01-01
    • 2016-03-04
    • 2020-08-06
    相关资源
    最近更新 更多