如何避免std::vector在(重新)分配时进行复制
How to avoid std::vector to copy on (re-)allocation?
我刚刚在向std::vector
添加新元素时遇到了一个问题。
似乎当你试图向它添加更多的元素,并且它需要分配更多的空间时,它通过复制
就我们而言,这不一定是真的。目前,我们可能在向量中有一些剩余的元素,因为我们选择暂时不删除它们,它们是有效的对象,但它们的数据不能保证有效的行为。对象有保护,但我从未考虑过向复制构造函数添加保护,因为我认为我们永远不会复制无效对象(矢量强制):
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
当我们处理完原始对象并将其放在向量中时,"member"恰好为null,所以当std::vector试图复制它时,它会崩溃。
是否可以阻止std::vector
复制该元素?还是我们必须防止可能的无效对象被复制?理想情况下,我们希望继续假设只创建了有效的对象,但这似乎意味着我们会立即从向量中清除它们,而不是等待并在稍后阶段执行。
它实际上是CopiedClass
中的一个需要修复的设计缺陷
std::vector
的行为符合预期并记录在案。
从你显示的小代码
member->DoSomething();
这表示您将获取一个指向任何内容的指针的浅层副本。
就我们而言,这不一定是真的。目前,我们可能在向量中有一些剩余的元素,因为我们选择暂时不删除它们,它们是有效的对象,但它们的数据不能保证有效的行为。
当我们处理完原始对象并将其放在向量中时,"member"恰好为null,所以当std::vector试图复制它时,它会崩溃。
由于您的复制构造函数在遵循"三条规则"方面没有正确处理这种情况,因此CopiedClass
的设计是错误的。
您应该创建member
的深度副本,或者使用智能指针(或普通实例成员),而不是原始指针。
智能指针应该正确地管理这些成员。
同样对于上面的代码片段,在盲目地取消引用和调用DoSomething()
之前,您应该针对nullpointer
进行测试。
是否可以阻止
std::vector
复制该元素?
当你要求c++11时,这是可能的,但要求你永远不要改变向量的大小,并为CopiedClass
提供一个移动构造函数和赋值运算符。
否则,std::vector
明确要求可复制类型(至少对于某些操作):
T必须满足CopyAssignable和CopyConstructable的要求。
您有责任正确地满足这些要求。
它通过复制当前保存的最后一个元素来实现这一点。
另请注意:在需要调整向量大小的情况下,所有现有元素都将被复制,而不仅仅是最后一个。
如果您事先知道矢量的最大大小,则解决此问题的解决方法将调用具有该最大大小的reserve
。当矢量的容量足够大时,不会发生重新分配,也不需要复制现有元素。
但这实际上只是一个变通办法。您应该重新设计您的类,使复制不会突然变成无效操作,无论是通过确保复制安全,还是通过禁止复制,并可能用移动(如std::unique_ptr
)代替它。如果您使复制的有效性取决于某些运行时状态,那么您甚至不需要std::vector
来遇到麻烦。一个简单的CopiedClass get();
将是一个潜在的错误。
当你试图为它添加更多元素时,它似乎需要分配更多的空间,它通过复制它的最后一个元素来实现目前持有。这似乎假设向量中的任何元素是完全有效的,因此复制总是成功的。
这两个句子都不太正确。当矢量空间不足时,它会执行重新分配,但对于,所有元素和这些元素都会被复制到新位置,或者可能会被移动。当您push_back
或emplace_back
,并且需要重新分配时,通常会复制元素:如果在此期间引发异常,则会将其视为从未添加过该元素。如果vector
检测到noexcept
用户定义的移动构造函数(MoveInsertable
概念),则元素被移动;如果此构造函数不是noexcept
,并且抛出异常,则结果未指定。
这些物体有防护装置,但我从未考虑过在复制构造函数,因为我认为我们永远不会复制无效的物体(矢量力):
vector
复制其value_type
:它不在乎包含的是有效还是无效您应该在复制控制方法中注意这一点,复制控制方法的作用域正是定义对象的传递方式。
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
显而易见的解决方案是检查member
是否有效并据此采取行动。
if (member.internalData.isValid())
member->DoSomething()
// acknowledge this.member's data is invalid
我们不知道member
是如何表示的,但Boost.可选是您可能想要的。
是否可以防止std::vector复制该元素?
Reallocation是vector
期望提交的东西,所以,不,你不能。reserv
占用空间可以避免这种情况,但维护代码来处理这种情况并不是很容易。更喜欢std::forward_list
/std::list
这样的容器。
其他解决方案:
- 持有
unique_ptr<decltype(member)>
,但指针通常不是真正的解决方案 - 定义对象的移动语义,如其他答案中所述
- 使用动态分配将 char* 复制到另一个字符**
- 强制复制分配超过移动分配运算符
- 使用不兼容的分配器复制分配无序列图
- std::元组分配和复制/移动异常保证
- C++成功复制动态分配的 obj 而不复制 ctor?
- 使用 Libssh 将文件从服务器复制到客户端:分配文件复制目标路径时出现问题
- 了解在 C++ 中分配容器时的对象复制
- 如何在C++中分配嵌套的protobuf而不进行复制
- 复制分配C++相同类型的 lambda
- C++ - 从移动分配运算符调用复制分配
- 不工作 复制分配运算符
- 我的程序运行良好,可以复制对象,但是当我使用复制分配(=)时,它仍然可以正常运行.为什么不给错误
- 是否可以在不分配内存或复制数据的情况下构造对象?
- 用于删除复制/移动分配运算符的有效签名
- C++移动分配可防止复制交换习惯用法
- 复制将 json-容器分配给矢量
- 复制分配shared_ptr导致分段错误?
- 使用新放置作为复制分配运算符不好吗?
- 不能分配 X 类型的对象,因为它的复制运算符隐式删除
- 是否有更好的方法来分配/复制shared_array