在向其添加新元素时,我偶然发现std::vector
的问题。
当您尝试向其添加更多元素时,它需要分配更多空间,它通过复制最后一个元素 当前包含的所有元素来实现。这似乎假设向量中的任何元素都是完全有效的,因此副本将始终成功。
在我们的案例中,这不一定是真的。目前我们可能在向量中有一些剩余元素,因为我们选择不删除它们,它们是有效对象,但它们的数据不保证有效行为。对象有防护,但我从未考虑过向复制构造函数添加防护,因为我认为我们永远不会复制无效对象(向量强制):
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
当我们完成原始对象并将其留在向量中时,“成员”被置为空,所以当std :: vector尝试复制它时,它会崩溃。
是否可以阻止std::vector
复制该元素?或者我们是否必须防止可能的无效对象被复制?理想情况下,我们想继续假设只创建了有效的对象,但这似乎意味着我们立即将它们从向量中清除,而不是等待并在稍后阶段进行。
答案 0 :(得分:7)
这实际上是CopiedClass
中需要修复的设计漏洞。
std::vector
表现得如预期和记录。
从您显示的小代码
member->DoSomething();
这表明你将把指针的浅表副本带到任何地方。
在我们的案例中,这不一定是真的。目前我们可能在向量中有一些遗留元素,因为我们选择不删除它们,它们是有效对象,但它们的数据不保证有效行为。
当我们完成原始对象并将其留在向量中时,“成员”被置为空,所以当std :: vector尝试复制它时,它会崩溃。
由于您的复制构造函数无法正确处理Rule of Three您的CopiedClass
设计错误。
您应该关心创建member
的深层副本,还是使用smart pointer(或普通实例成员),而不是原始指针。
智能指针应正确处理这些成员的管理。
同样对于上面的代码段,您应该在盲目取消引用并调用nullpointer
之前对DoSomething()
进行测试。
是否可以阻止
std::vector
复制该元素?
正如您要求c++11这样可能,但要求您永远不要更改向量的大小,并为CopiedClass
提供move constructor和assignment operator。< / p>
否则,std::vector
explicitly requires可复制类型(至少对于某些操作):
T必须符合CopyAssignable和CopyConstructible的要求。
您有责任正确满足这些要求。
...它通过复制当前持有的最后一个元素来实现。
另请注意:在需要调整矢量大小的情况下,将复制现有元素的所有,而不仅仅是 last one 。
答案 1 :(得分:2)
如果您事先知道了向量的最大大小,则此问题的解决方法将调用具有该最大大小的reserve
。当向量的容量足够大时,不会发生重新分配,也不需要创建现有元素的副本。
但这真的只是一种解决方法。您应该重新设计您的班级,以便复制不会突然变为无效操作,无论是通过安全复制还是禁止复制,或者可能将其替换为移动,例如std::unique_ptr
。如果您使复制的有效性取决于某些运行时状态,那么您甚至不需要std::vector
来解决问题。一个简单的CopiedClass get();
将是一个潜在的错误。
答案 2 :(得分:2)
当您尝试向其添加更多元素时,它似乎需要 分配更多空间,它通过复制最后一个元素来实现 目前持有。这似乎假设向量中的任何元素 完全有效,因此副本将始终成功。
这两个句子都不正确。当向量空间不足时,是的,执行重新分配,但是所有元素,这些元素被复制到新位置或可能移动。当您需要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.Optional是您可能正在寻找的。
是否可以阻止std :: vector复制该元素?
重新分配是vector
期望提交的内容,所以,不,你不能。 reserv
- 空间可以避免这种情况,但维护代码来处理这种情况并不是真的无痛。相反,更喜欢像std::forward_list
/ std::list
这样的容器。
其他解决方案:
unique_ptr<decltype(member)>
,但指针通常不是真正的解决方案