例如,来自C ++参考文献中的 std :: deque :: operator = :
(1)复制分配 (const std :: deque& other)
用其他内容的副本替换内容。
如果 std :: allocator_traits :: propagate_on_container_copy_assignment()是 是的,目标分配器由源的副本替换 分配器。如果目标和源分配器不进行比较 等于,目标(* this)分配器用于释放内存, 然后其他的分配器用于在复制之前分配它 元件。
如果this->get_allocator() == other.get_allocator()
,我可以简单地销毁和取消分配this
'元素(如果需要),或者根据需要分配和构造元素,或者根据需要将元素从other
复制分配给*this
。
但如果不是呢?上面的引用是否意味着我无法复制 - 分配元素,所以我必须首先使用this->get_allocator()
销毁和释放所有元素,然后使用{{1}分配和构造元素}?
但如果是这种情况,我为什么要使用other.get_allocator()
进行分配呢?
由于other.get_allocator()
无法正确释放内存,因此不会在以后导致运行时错误吗?
(2)移动作业 (std :: deque&& other)
将内容替换为其他内容 使用移动语义(即其他数据从其他数据移入 这个容器)。其他是后来处于有效但未指定的状态。 如果 的std :: allocator_traits :: propagate_on_container_move_assignment() 如果是,则目标分配器将替换为源的副本 分配器。如果为false,则源和目标分配器执行此操作 不比较平等,目标不能取得所有权 内存,必须移动 - 分配每个元素,分配 根据需要使用自己的分配器的额外内存。无论如何,全部 最初存在于*中的元素要么被销毁要么被替换 按元素移动分配。
如果this
,这是一项简单的任务
但如果没有,则上面会出现同样的问题,除非在这种情况下使用移动分配。
在这两种情况下,我还有一个问题 如果元素既不能复制分配也不能移动分配,是否可以销毁它并从其他元素构建?如果是,我应该使用谁的分配器?
答案 0 :(得分:6)
POCCA(传播容器拷贝分配)分配器被复制分配为容器的拷贝分配的一部分。同样,在分配容器的移动时,移动分配POCMA分配器。
上面的引用是否意味着我无法复制 - 分配元素,所以我必须首先使用
this->get_allocator()
销毁和释放所有元素,然后使用{{1分配和构造元素}}?
正确。
但如果是这种情况,我为什么要使用
other.get_allocator()
进行分配呢?以后不会导致运行时错误,因为other.get_allocator
不会正确释放内存吗?
因为赋值传播了分配器:赋值后,this->get_allocator()
是this->get_allocator()
的副本,所以它可以安全地释放由它分配的内存。
如果
other.get_allocator()
,这是一项简单的任务。但如果没有,则上面会出现同样的问题,除非在这种情况下使用移动分配。
实际上,这完全不同。使用POCMA分配器移动赋值是微不足道的:您销毁this->get_allocator() == other.get_allocator()
中的所有元素,释放内存,并掠夺*this
的内存和分配器。
容器移动分配必须采用逐元素移动分配/构造的唯一情况是,当您有非POCMA 分配器且分配器比较不相等时。在这种情况下,所有分配和构造都使用other
完成,因为您不传播任何内容。
在这两种情况下,我还有一个问题。如果元素既不能复制分配也不能移动分配,是否可以销毁它并从其他元素构建?如果是,我应该使用谁的分配器?
使用最初构造的分配器来销毁它;使用分配器构造它将被销毁。换句话说,如果要传播分配器,则使用目标分配器销毁它,并使用源分配器进行构造。
答案 1 :(得分:3)
我正在回答我自己的问题,以显示我得到了什么。 --Dannyu NDos,2017年1月16日
在复制或移动作业中,其行为取决于两个条件:
1。是分配器比较相等吗? (也就是说,源分配器是否能够销毁和释放目标容器的元素?)
2。在容器分配期间,源的分配器是否传播(=被分配给目标)?
副本分配:
A. 如果分配器比较相等:
可以安全地将元素直接复制分配给元素
由于分配器已经比较相等,因此分配器是否传播并不重要。如果需要构建或销毁任何元素,那么分配器对其进行操作也无关紧要
B。如果分配器不比较相等:
B.a。如果分配器没有传播:
可以安全地将元素直接复制到元素,但是如果需要构造或销毁任何元素,源分配器必须这样做,因为它只能破坏目标容器的元素。
B.b。如果分配器传播:
首先,目标分配器必须对所有目标容器的元素进行破坏和解除分配
然后分配器传播,然后源分配器分配和复制构造所有源容器的元素。
移动任务:
A. 如果分配器比较相等:
目标容器擦除其所有元素,然后获取源容器元素的所有权。这需要O(1)时间
B。如果分配器不比较相等:
B.a。如果分配器没有传播:
可以安全地完成向元素直接移动分配元素,但是如果需要构造或销毁任何元素,源分配器必须这样做,因为它只能破坏源容器的元素。这需要O(n)时间。分配后,源容器必须处于有效状态
B.b。如果分配器传播:
首先,目标分配器必须对所有目标容器的元素进行破坏和解除分配
然后分配器传播,然后源分配器分配并移动构造所有源容器的元素。这需要O(n)时间。分配后,源容器必须处于有效状态。
在源代码中,给定alloc
是容器的分配器,Alloc
是它的类型,它们通常是这样编写的:
/*container*/ &operator = (const /*container*/ &other) {
if (std::allocator_traits<Alloc>::propagate_on_container_copy_assignment::value && alloc != other.alloc) {
clear();
alloc = other.alloc;
// directly copy-constructs the elements.
} else {
// directly copy-assigns the elements.
// alloc does all allocation, construction, destruction, and deallocation as needed.
}
return *this;
}
/*container*/ &operator = (/*container*/ &&other)
noexcept(std::allocator_traits<Alloc>::is_always_equal::value) {
if (alloc == other.alloc) {
clear();
// *this takes ownership of other's elements.
} else if (std::allocator_traits<Alloc>::propagate_on_container_move_assignment::value) {
clear();
alloc = other.alloc;
// directly move-constructs the elements.
} else {
// directly move-assigns the elements.
// alloc does all allocation, construction, destruction, and deallocation as needed.
}
// the source container is made valid, if needed.
return *this;
}