C++中的realloc等价函数

7
是的,又是一个关于realloc和std::vector的问题。我知道你会说什么,我也同意,忘记手动内存分配,只需使用std::vector即可。不幸的是,我的教授禁止我在这个作业中使用STL中的任何东西。
所以,是的,我有一个动态数组T,我需要它可以调整大小,但我不能使用std::vector。我可以回到黑暗时代并用malloc等函数完成整个过程,但如果我能使用new就太棒了。
我已经阅读了很多帖子,每个人都说“不,你不能这样做,使用std::vector”,但它们都是在2011年8月之前发布的,我希望自C++11诞生以来可能发生了一些变化。所以告诉我,我有运气吗,还是必须恢复到C风格的内存分配?

8
很遗憾,我的教授禁止我在这个作业中使用STL的任何内容。——没问题,std::vector不在STL中,它属于C++标准库。 - user529758
1
@Rapptz:如果你想让你的代码真正起作用,那么你就不能这样做。 - Nicol Bolas
4
@MichaelDorst所提到的H2CO3是指STL是一个过时的第三方库的旧名称,而现在被错误地认为是STL的部分实际上都是C++标准库的一部分。如果你感兴趣的话,可以查看这篇文章:STL与“C++标准库”之争 - Karthik T
2
@perreal 你的意思是使用 malloc 然后再复制,有时候确实是这样的,但如果有足够的空间,realloc 不会复制,它只会分配更多的空间。 - Michael Dorst
1
@jogojapan,你想告诉我的东西听起来很有前途,能否发一篇回答更详细地解释一下呢? - Michael Dorst
显示剩余13条评论
2个回答

11
无论如何都应该避免使用realloc,因为您不能像那样移动C++对象。
  • 使用buf = new unsigned char[sizeof(T) * capacity]创建新缓冲区
  • 将分配的unsigned char *强制转换为T *,并从现在开始使用这些T-指针
  • 通过"placement new"构建新元素,例如new (&buf[i]) T(original_copy)
  • 要将缓冲区复制到较大的缓冲区中,请先分配新缓冲区,然后使用std::uninitialized_copy(而不是std::copy),接着使用buf[i].~T()销毁旧缓冲区中的元素,并使用delete [] buf释放旧缓冲区。

所有这些都假设您不必担心异常安全性,对于本任务来说可能没问题。
但请注意,在实际代码中,您必须保证异常安全性,而且比这个更加繁琐。


3
不好!使用static_cast<T*>(::operator new(sizeof(T) * cap))获取内存。然后使用定位new将对象放入其中。这样,您可以使用普通指针算术来标记第一个、最后一个和结尾。我甚至会说,对于一个任务来说这样做过于复杂了,直接使用new T[capacity]并要求默认构造函数即可。 - Xeo
@Xeo:我不明白,为什么new unsigned char[]不好,而::operator new()又怎么更好呢?关于默认构造函数:如果他选择这条路,那么他还需要使用std::copy - user541686
2
这个想法是你有明确类型的内存。 - Xeo
@Xeo:哦,我明白我回答为什么让你感到困惑了——我并不是说他每次访问元素时都应该执行转换,我只是意味着他在访问前不应该忘记进行类型转换。他确实会存储三个指针,这些指针最初从unsigned char *强制转换而来,但是指针将是T *,我相信这与您的意图相同。我会在我的帖子中澄清这一点。 - user541686
2
关于“你不能像那样移动C++对象”的问题,虽然std::vector通常无法使用realloc,但它确实可以像那样移动C++对象。问题不在于realloc,而在于容器设计(需要简单的标准分配器)。对于适当的容器,realloc将提供非常好的性能提升,没有问题(比std::vector的实现问题更大)。 - Cheers and hth. - Alf
显示剩余2条评论

8
realloc存在一个问题,它可能会将现有数据移动到不同连续地址范围内。如果需要这样做,由于它是C函数,因此数据将被复制,而不考虑C++对象的生命周期:
  • 不使用复制/移动构造函数
  • 源对象的析构函数不会在之后调用
这可能会导致严重的后果——例如,当要移动的对象包含指向将被释放的内存区域中的地址的指针/引用时。
遗憾的是,普通的malloc 实现不允许回调挂钩,因此您无法用自己的C ++ 安全实现替换内存内容复制代码。如果您下定决心,可以尝试寻找更灵活的“malloc”库,但这不太值得麻烦和风险。
因此,在一般情况下,您应该使用new来更改容量、复制/移动每个对象,然后在之后使用delete删除原对象。
如果您确定您的数据足够简单,以至于memcpy样式的重定位不会导致不良后果,则可以使用realloc(自担风险)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接