简体   繁体   English

C++ 提升优先级队列行为

[英]C++ Boost Priority Queue Behavior

I tried to find out exactly how the boost priority queue is implemented and I am confused.我试图弄清楚提升优先级队列是如何实现的,我很困惑。

Header file (main.hpp): Header 文件(main.hpp):

#ifndef MAIN_HPP
#define MAIN_HPP

#include <cstddef>
#include <cstdint>
#include <iostream>

#include <boost/heap/priority_queue.hpp>

typedef std::int32_t int32_t;

typedef struct st {
    int32_t num;
    int32_t f;

    st() {
        std::cout << "DEFAULT" << std::endl;
        this->f = 0;
    }

    st(int32_t num) {
        std::cout << "creation\t" << num << std::endl;
        this->num = num;
        this->f = 0;
    }

    ~st() {
        std::cout << "del\t" << num << std::endl;
        f = 1;
    }
} st;

typedef struct st_c0 {
    bool operator()(const st& st0, const st& st1) const {
        return (st0.num > st1.num);
    }
} st_c0;

typedef struct st_c1 {
    bool operator()(const st* st0, const st* st1) const {
        return (st0->num > st1->num);
    }
} st_c1;

#endif
#include "main.hpp"

int main() {
    boost::heap::priority_queue<st, boost::heap::compare<st_c0>> q0;
    boost::heap::priority_queue<st*, boost::heap::compare<st_c1>> q1;
    st y = st(5);
    q0.push(st(44));
    q0.push(y);
    q0.empty();
    std::cout << y.f << std::endl;
    return 0;
}

The output I get is:我得到的 output 是:

creation        5
creation        44
del     44
del     44
del     44
del     44
del     44
del     5
del     5
del     5
0
del     5
del     5
del     44

The order of object creation and deletion does not make sense. object 创建和删除的顺序没有意义。 How do the internals of the priority queue work, and what is the best practice for them (storing pointers vs storing objects)?优先级队列的内部是如何工作的,它们的最佳实践是什么(存储指针与存储对象)?

You aren't accounting for the copy-constructor that will automatically be created for your class.您没有考虑将为您的 class 自动创建的复制构造函数。

In your case you wouldn't automatically get a move-constructor, but it's still nice to see where the compiler could do a move instead of a copy.在您的情况下,您不会自动获得移动构造函数,但是很高兴看到编译器可以在哪里进行移动而不是复制。

If you change your st to eg:如果您将st更改为例如:


struct st {
    int32_t num;
    int32_t f;

    st() {
        std::cout << this << "\tctor default" << std::endl;
        this->f = 0;
    }

    st(int32_t num) : num(num), f(0) {
        std::cout << this << "\tctor num\t" << num << std::endl;
    }

    st(st const& other) : num(other.num), f(other.f) {
        std::cout << this << "\tctor copy\t" << num << "\t (from " << &other << ")" << std::endl; 
    }

    st(st&& other): num(other.num), f(other.f) {
        std::cout << this << "\tctor move\t" << num << "\t (from " << &other << ")" << std::endl;
    }

    st& operator=(st const& other) {
        num = other.num;
        f = other.f;
        std::cout << this << "\tassign copy\t" << num << "\t (from " << &other << ")" << std::endl;
        return *this;
    }

    st& operator=(st&& other) {
        num = other.num;
        f = other.f;
        std::cout << this << "\tassign move\t" << num << "\t (from " << &other << ")" << std::endl;
        return *this;
    }

    ~st() {
        std::cout << this << "\tdtor\t\t" << num << std::endl;
    }
};

godbolt example螺栓示例

you'll get a better picture of what's going on:你会更好地了解正在发生的事情:

// construct y
0x7fffd8f3b1e8  ctor num    5
// call to push(st(44))
0x7fffd8f3b238  ctor num    44
0x7fffd8f3b1b4  ctor copy   44   (from 0x7fffd8f3b238)
0x97cec0        ctor move   44   (from 0x7fffd8f3b1b4)
0x7fffd8f3b1b4  dtor        44
0x7fffd8f3b164  ctor move   44   (from 0x97cec0)
0x7fffd8f3b178  ctor move   44   (from 0x7fffd8f3b164)
0x97cec0        assign move 44   (from 0x7fffd8f3b178)
0x7fffd8f3b178  dtor        44
0x7fffd8f3b164  dtor        44
0x7fffd8f3b238  dtor        44
// call to push(y)
0x7fffd8f3b1b4  ctor copy   5    (from 0x7fffd8f3b1e8)
0x97cee8        ctor move   5    (from 0x7fffd8f3b1b4)
0x97cee0        ctor copy   44   (from 0x97cec0)
0x97cec0        dtor        44
0x7fffd8f3b1b4  dtor        5
0x7fffd8f3b164  ctor move   5    (from 0x97cee8)
0x7fffd8f3b178  ctor move   5    (from 0x7fffd8f3b164)
0x97cee8        assign move 44   (from 0x97cee0)
0x97cee0        assign move 5    (from 0x7fffd8f3b178)
0x7fffd8f3b178  dtor        5
0x7fffd8f3b164  dtor        5
// after main()
0x7fffd8f3b1e8  dtor        5
0x97cee0        dtor        5
0x97cee8        dtor        44

So to break it down:所以分解它:

    1. pushing the first element推动第一个元素
    • your st is constructed and after that copied & moved a few times.您的st已构建,然后复制并移动了几次。
    • it finally ends up in 0x97cec0 (the allocated storage from the heap)它最终以0x97cec0结束(从堆中分配的存储)
    1. pushing the second element推动第二个元素
    • the second call triggers a resize, so the 44 must be moved to a new allocation第二次调用触发调整大小,因此必须将 44 移动到新分配
    • the 5 also gets copied & moved a bit 5 也被复制和移动了一点
    • the 5 & 44 are swaped into place, so the priority queue is correctly sorted 5 和 44 被交换到位,因此优先级队列正确排序
    1. empty()
    • does nothing (would return true, because the container contains elements)什么都不做(会返回 true,因为容器包含元素)
    • if you want to delete all elements use clear() .如果要删除所有元素,请使用clear()
    1. after main returns主要回报后
    • y gets destructed y 被破坏
    • the priority queue gets destructed and calls the destructor for both st 's优先级队列被破坏并为两个st调用析构函数

There are no guarantees though how many copies / moves the implementation of boost::heap::priority_queue<st, boost::heap::compare<st_c0>> does, so this might change at any time.尽管boost::heap::priority_queue<st, boost::heap::compare<st_c0>>的实现没有保证多少副本/移动,所以这可能随时改变。


Pointers vs Objects指针与对象

In general you would use objects whenever they are small and easy to copy / move.通常,只要对象很小且易于复制/移动,您就会使用它们。

If you use pointers your object won't be copied or moved at all, only the pointer to it, so this would be better if your objects are large and / or expensive to copy / move.如果您使用指针,您的 object 根本不会被复制或移动,只有指向它的指针,所以如果您的对象很大和/或复制/移动成本很高,这会更好。

But with bare pointers you also need to delete them manually, if possible i would recommend to use smart pointers instead, eg:但是对于裸指针,您还需要手动delete它们,如果可能的话,我建议使用智能指针,例如:

boost::heap::priority_queue<boost::shared_ptr<st>, boost::heap::compare<TODO>> q0;

that way your ``st`'s autmatically get freed & you don't have to manually delete them.这样你的“st”就会自动被释放,你不必手动删除它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM