如何实现分配器感知容器分配?

时间:2016-11-25 09:34:14

标签: c++ c++11 memory-management assign allocator

例如,来自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,这是一项简单的任务  但如果没有,则上面会出现同样的问题,除非在这种情况下使用移动分配。

在这两种情况下,我还有一个问题 如果元素既不能复制分配也不能移动分配,是否可以销毁它并从其他元素构建?如果是,我应该使用谁的分配器?

2 个答案:

答案 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;
}