手打配对堆模板(支持push, pop, top, join)

本文介绍了一种使用C++模板实现的配对堆数据结构。该实现支持任意类型元素的存储,只要这些类型定义了小于运算符或者提供了一个比较器。此外,文章提供了完整的类定义,包括插入、获取最小元素、删除最小元素等核心操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 常数较二叉堆小。
  • 采用 new、delete 分配内存,不喜勿喷。
  • 支持任意类型(需定义 operator< 或传入比较器)(需要默认构造函数)

如有错请指正!谢谢!

template<typename T, typename Comp = less<T>>
class pairing_heap
{
    public:
        typedef Comp comparer;
        
        pairing_heap() : _r(nullptr) { }
        ~pairing_heap() { while(!empty()) pop(); }
        
        void push(T o)
        {
            _Node *n = new _Node;
            n->v = o, n->son = n->sib = nullptr;
            _r = _merge(_r, n);
        }
        
        T top()
        {
            return _r ? _r->v : T();
        }
        
        void pop()
        {
            _Node *nr = _multi_merge(_r->son);
            if(_r) delete _r;
            _r = nr;
        }
        
        bool empty()
        {
            return !_r;
        }
        
        void join(pairing_heap<T, Comp> &p)
        {
            _r = _merge(_r, p._r);
            p._r = nullptr;
        }
        
    private:
        struct _Node
        {
            T v;
            _Node *son, *sib;
        } *_r;
        comparer _C;
        
        _Node *_merge(_Node *a1, _Node *a2)
        {
            if(!a1 || !a2) return a1 ? a1 : a2;
            if(_C(a1->v, a2->v)) swap(a1, a2);
            _Node *s = a1->son;
            if(!s) a1->son = a2;
            else
            {
                a2->sib = s->sib;
                s->sib = a2;
            }
            return a1;
        }
        
        _Node *_multi_merge(_Node *a)
        {
            _Node *b, *c;
            if(!a) return nullptr;
            if(!a->sib) return a;
            b = a->sib, a->sib = nullptr;
            if(!b->sib) return _merge(a, b);
            c = b->sib, b->sib = nullptr;
            return _merge(_merge(a, b), _multi_merge(c));
        }
};

转载于:https://www.cnblogs.com/js2xxx/p/9437465.html

<think>好的,我现在需要回答用户关于pthread_cleanup_push的功能、用法和示例的问题。首先,我得回忆一下POSIX线程中清理函数的相关知识。根据用户提供的引用内容,特别是引用[2]和引用[3],pthread_cleanup_push和pthread_cleanup_pop用于在线程终止时自动释放资源。用户可能想知道如何正确使用这对函数,以及它们的工作原理。 首先,我应该解释功能部分。根据引用[2],这对函数的作用是在线程终止时执行清理函数,比如释放资源,如内存、文件句柄等。需要说明的是,清理函数会在调用pthread_exit或到达取消点时执行,而正常返回线程函数的话,可能不会触发,除非pop时参数设为非零。 接下来是用法。根据引用[3],这对函数以栈的方式管理清理函数,先入后出。需要强调pushpop必须成对出现,且在同一作用域内。此外,execute参数的作用需要详细说明:当pop的参数为非零时,会执行清理函数;如果为零,则只有在线程被取消或调用pthread_exit时才会执行。用户可能需要知道何时使用哪种参数。 然后是示例部分。用户提供的引用[1]中有一个示例,但可能需要简化或解释。例如,在线程函数中两次push,然后两次pop,其中第二个pop的参数为1,所以会执行。需要说明示例中资源释放的顺序,以及为何第一个pop的参数是0或1的区别。此外,引用[4]中的示例涉及线程取消,可以提到如何与清理函数结合使用,比如在取消点或调用pthread_exit时触发清理函数。 还需要注意用户可能的疑问点,比如为什么即使pop的参数为0,线程终止时也会执行清理函数。根据引用[3],参数为0时,只有在异常终止时才会执行。而正常执行到pop时不会执行,除非参数非零。这点需要明确区分。 另外,用户可能关心实际应用中的常见错误,比如忘记成对使用pushpop,或者作用域不正确导致编译错误。需要提醒必须严格配对,通常在同一代码块中使用。 最后,总结关键点,确保用户理解清理函数的执行时机、栈结构的管理方式以及参数的影响。同时,提供示例代码的解释,说明每个步骤的作用,比如push的顺序和pop时的执行情况。</think>### pthread_cleanup_push 功能与用法详解 #### 一、核心功能 `pthread_cleanup_push` 和 `pthread_cleanup_pop` 是 POSIX 线程库中用于**资源自动清理**的关键函数对,主要功能如下: 1. **注册清理函数**:将用户定义的清理函数(如释放内存、关闭文件等)压入线程的**清理函数栈**。 2. **异常安全保证**:在以下场景自动执行清理函数: - 线程调用 `pthread_exit` 显式终止 - 线程被其他线程通过 `pthread_cancel` 取消 - 执行到**取消点**(如 `printf`, `sleep` 等系统调用)[^4] 3. **栈式管理**:遵循**后进先出**(LIFO)顺序执行清理函数[^3]。 #### 二、函数原型与参数 ```c void pthread_cleanup_push(void (*routine)(void*), void *arg); void pthread_cleanup_pop(int execute); ``` - **`routine`**:清理函数指针,需定义 `void cleanup(void *arg)` 格式 - **`arg`**:传递给清理函数的参数 - **`execute`**:控制是否立即执行清理函数 - **非 0**:弹出栈顶函数并执行 - **0**:仅弹出栈顶函数,不执行(仅在异常终止时执行) #### 三、使用规则 1. **严格作用域配对**:必须成对使用,且在同一代码块层级(类似 `{}` 作用域)[^2]。 2. **执行逻辑**: ```c pthread_cleanup_push(func1, arg1); // 压栈 func1 pthread_cleanup_push(func2, arg2); // 压栈 func2 /* ... 代码段 ... */ pthread_cleanup_pop(1); // 弹栈 func2 并执行 pthread_cleanup_pop(0); // 弹栈 func1 不执行(仅在异常时触发) ``` 3. **典型应用场景**: - 动态内存释放(`malloc`/`free`) - 文件描述符/网络连接关闭 - 锁的释放(如互斥锁 `pthread_mutex_unlock`) --- #### 四、示例代码解析 基于引用[1]的示例简化: ```c #include <pthread.h> #include <stdio.h> void cleanup(void *arg) { printf("清理资源: %s\n", (char*)arg); } void* thread_func(void *arg) { pthread_cleanup_push(cleanup, "资源A"); // 压栈 pthread_cleanup_push(cleanup, "资源B"); // 再次压栈 if (arg) printf("收到参数: %s\n", (char*)arg); pthread_cleanup_pop(1); // 弹栈并执行清理"资源B" pthread_cleanup_pop(0); // 弹栈但不执行"资源A"(除非线程异常终止) return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_func, "测试数据"); pthread_join(tid, NULL); printf("主线程结束\n"); return 0; } ``` ##### 输出结果分析: ``` 收到参数: 测试数据 清理资源: 资源B 主线程结束 ``` - **为什么只执行了"资源B"的清理?** - 第二个 `pthread_cleanup_pop(1)` 触发"资源B"的执行 - 第一个 `pthread_cleanup_pop(0)` 仅弹出"资源A"不执行(线程正常返回未触发异常) --- #### 五、关键注意事项 1. **线程正常返回 VS 异常终止**: - 若线程通过 `return` 正常退出,**仅当 `pthread_cleanup_pop` 参数非 0 时才会执行清理函数**。 - 若线程通过 `pthread_exit` 终止,**无论参数是否为 0,所有未弹出的清理函数都会执行**[^3]。 2. **与取消点的配合**: - 在可能阻塞的代码(如 `printf`)前后建议添加 `pthread_testcancel()` 以响应取消请求[^4]。 3. **避免内存泄漏**: - 确保所有 `push` 都有对应的 `pop`,否则可能导致栈不平衡或资源泄漏。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值