将字符串存储在c++中的稳定内存中
store strings in stable memory in c++
先有一点背景(如果对此感到无聊,请跳到粗体)。
我正试图把两段代码粘在一起。一个是大量使用自定义字符串视图对象的JSON/YML库,另一个是2000年代初的一段代码。
很长一段时间以来,我一直看到奇怪的行为,直到我将其追溯到内存问题,即我在JSON/YML库中构建的字符串视图以const char*
作为构造函数,并假设该char数组的内存位置在字符串视图的整个生命期内保持不变。然而,我构建这些视图的一些std::string
对象是临时的,所以这不是真的,字符串视图最终指向垃圾。
现在,我认为我很聪明,以std::vector
的形式构建了一个缓存,可以容纳所有临时字符串,我会在这些字符串上构建字符串视图,最后只清除缓存——这很容易。
然而,我仍然时不时地看到混乱的字符串,直到我找到原因:有时,当将东西推到超出预先分配大小的向量时,向量会被移动到不同的内存位置,从而使所有字符串视图无效。目前,我已经决定预先分配一个足够大的缓存大小,以避免矢量的任何可能的移动,但我可以看到,这会在未来的大型运行中造成严重且不可传输的问题。所以我的问题是:
如何构造一个std::vector<std::string>
或任何其他字符串容器,以避免在内存中一起移动,或者在发生这种情况时至少抛出一条错误消息
当然,如果你觉得我在根本上以错误的方式处理整个问题,也请告诉我应该如何处理这个问题。
如果您感兴趣,有问题的两段代码是RapidYAML和CERN统计库ROOT。
我对类似问题的回答是:当矢量改变功能时,有什么方法可以更新指针/参考值?
如果将向量中的对象存储为std::unique_ptr或std::shared_ptr,则可以使用std::unique_ptr::get()获取指向底层对象的观察指针(如果取消引用智能指针,则为引用)。这样,即使智能指针的内存位置在调整大小时发生变化,观察指针也指向相同的对象,从而指向相同的内存位置。
[…]有时,当将东西推送到超出预先分配大小的向量时,向量会被移动到不同的内存位置,从而使所有字符串视图无效。
原因是std::vector
需要将其数据连续存储在内存中。因此,如果在添加元素时超过了向量的最大容量,它将在内存中分配一个新空间(这次足够大),并将所有数据移动到这里。
您受到的影响称为迭代器无效。
如何构建一个std::vector或任何其他字符串容器,以避免在内存中一起移动,或者在发生这种情况时至少抛出一条错误消息?
您至少有3个简单的解决方案:
- 如果您的缓存大小应该是固定的,并且在编译时已知,我建议您改用
std::array
- 如果您的缓存大小应该是固定的,但在编译时不一定是已知的,我建议您将
std::vector
所需的容量reserve()
,这样您就可以保证它足够大,不需要重新分配 - 如果您的缓存大小可能会更改,我建议您改用
std::list
。它被实现为一个(通常是双重)链表。它将保证元素不会在内存中重新定位
但由于它们不是连续存储在内存中的,您将失去直接访问任何元素的能力(即,您需要遍历列表才能找到元素)
当然,可能还有其他解决方案(我并不认为这个答案是详尽无遗的),但这些解决方案将允许您几乎不更改代码(仅更改容器),并保护您的字符串视图无效。
也许可以使用std::list。它的访问方法较慢(至少在迭代时),但内存位置是恒定的。两者的原因都是它不使用连续内存。
或者,创建一个包装器,该包装器包装指向使用"new"创建的字符串的指针。该地址也将保持不变。编辑:不知怎么的,我忽略了我刚才描述的几乎是一个智能指针减去自动删除;)
遗憾的是,在确保内容至少在经典操作系统上保持不变的同时,不可能增长向量。
有一个函数realloc试图保持不变,但正如你在文档中所读到的,这并不能保证,只有操作系统会决定。
为了解决你的问题,你需要一个池的概念,这里是一个字符串池,用来处理字符串的使用寿命。
您可能只需要一个简单的字符串std::list
,但它会导致糟糕的数据混叠和大量的独立分配,这对您的性能不利。这些也将是智能指针的问题。
因此,如果你关心性能,在我看来,你如何在你的案例中实现它可能与你目前的实现不远。因为您无法调整向量的大小,所以您应该更喜欢在编译时决定的固定大小的std::array
。然后,只要你需要,你就可以创建一个新的来扩展你的内存容量。这通常可以通过CCD_ 12来容易地实现。
我不知道它是否适用于这里,但如果你的应用程序在执行过程中可以创建任意数量的字符串,你必须小心,因为这可能会导致不断增长的内存池,最终可能会导致内存问题。为了解决这个问题,您可以确保不再使用的字符串可以被重用或释放。遗憾的是,我在这里帮不了你太多,因为这些规则将取决于你的申请。
- 将字符串存储在c++中的稳定内存中
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- Win32编译器选项和内存分配
- 当vector是tje全局变量时,c++中vector的内存管理
- 带内存和隔离功能的SQLite
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 迭代时从向量和内存中删除对象
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 为什么示例代码访问IUnknown中已删除的内存
- 如何在C++类内存结构中创建"spacer"?
- 从构造函数抛出异常时如何克服内存泄漏
- malloc() 可能出现内存泄漏
- 如何理解将半精度指针转换为无符号长指针和相关的内存对齐
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 如何针对特定情况调试和修复此双自由内存损坏问题
- 类型总是使用其大小存储在内存中吗
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存