C++编译报错:use of deleted function ‘pQueue::pQueue(const pQueue&)’ 原因解析及解决方法求助
嘿,作为C++新手碰到这个错误很正常,我来给你掰扯清楚到底咋回事,以及怎么解决。
首先先翻译一下你看到的错误:
error: use of deleted function ‘pQueue::pQueue(const pQueue&)’
note:is implicitly deleted because the default definition would be ill-formed
人话版本就是:你代码里某个地方在尝试复制一个pQueue对象,但C++本来应该自动帮你生成的“拷贝构造函数”被禁用了,用不了。为啥会被禁用?因为你的pQueue类里大概率藏着不能被复制的东西——比如std::thread(线程对象是不能复制的,总不能把一个正在跑的线程复制成另一个吧),C++发现这种情况,就直接把默认的拷贝构造函数给“删了”,不让你用。
接下来看看你代码里的几个核心问题,正是这些问题导致了错误,同时也让你的队列类根本没起到作用:
问题1:你的队列是个“一次性”局部变量,不是类的成员
你看pQueue的构造函数:
pQueue::pQueue() { std::priority_queue<std::pair<int, Category*>> q; }
这里的q只是构造函数里的临时变量,构造函数跑完就没了!你的queueWork和doWork函数用的都是自己参数里的队列,完全和这个临时变量没关系——相当于你写了个队列类,但类里根本没存队列,这哪行啊?
问题2:函数参数传值方式完全错了
你的queueWork和doWork都把std::priority_queue作为传值参数:
void queueWork(std::priority_queue<std::pair<int, Category*>> q, int action, Category* data)
传值意味着每次调用函数都会复制整个队列,不仅效率低,而且你改的是复制后的临时队列,原队列根本不会变!更糟的是,如果队列关联了不能复制的东西(比如线程),就会触发你看到的错误。
问题3:非静态成员函数调用错了
在main.cpp里你写了:
pQueue::queueWork(Queue, action, data);
queueWork是pQueue的非静态成员函数,必须通过pQueue的对象来调用,比如Queue.queueWork(action, data);,你把对象当参数传进去,不仅类型不匹配,还会触发对象拷贝,直接撞到那个“拷贝构造函数被删除”的错误上。
现在给你一步步改代码,解决所有问题
第一步:修复pQueue类的结构
先在file.h里给pQueue加上真正的成员变量——队列、线程、互斥锁(多线程操作队列必须加锁,不然会乱套):
#pragma once #include <queue> #include <thread> #include <mutex> // 前置声明Category,避免头文件依赖 class Category; class pQueue { public: pQueue(); ~pQueue(); // 删掉多余的队列参数,直接操作类里的成员队列 void queueWork(int action, Category* data); private: // 同样删掉队列参数 void doWork(); // 类的核心:成员队列 std::priority_queue<std::pair<int, Category*>> queue_; // 执行doWork的工作线程 std::thread worker_thread_; // 保护队列的互斥锁,防止多线程抢着改队列 std::mutex queue_mutex_; // 用来停止线程的标志 bool stop_flag_ = false; };
第二步:实现构造和析构函数
在file.cpp里,构造函数启动工作线程,析构函数安全停止线程:
#include "File.h" #include <utility> pQueue::pQueue() : stop_flag_(false) { // 启动线程,让线程执行当前对象的doWork函数 worker_thread_ = std::thread(&pQueue::doWork, this); } pQueue::~pQueue() { // 设置停止标志,告诉线程可以退出了 stop_flag_ = true; // 等待线程执行完再销毁对象 if (worker_thread_.joinable()) { worker_thread_.join(); } }
第三步:修复queueWork和doWork函数
现在这两个函数直接操作类里的成员队列,并且用互斥锁保护:
void pQueue::queueWork(int action, Category* data) { // 加锁,确保同一时间只有一个线程在改队列 std::lock_guard<std::mutex> lock(queue_mutex_); // 把任务加入队列 queue_.emplace(action, data); } void pQueue::doWork() { // 线程循环,直到收到停止信号 while (!stop_flag_) { std::pair<int, Category*> task; { // 加锁访问队列 std::lock_guard<std::mutex> lock(queue_mutex_); // 如果队列为空,就跳过这次循环,避免空转 if (queue_.empty()) { continue; } // 取出队列顶部的任务 task = queue_.top(); queue_.pop(); } // 锁在这里会自动释放,不用手动解锁 // 这里处理你的任务 Category* data = task.second; // 比如调用你的处理逻辑:bool result = function(data); // TODO: 处理任务结果,比如存到线程安全的容器里 } }
第四步:修复main.cpp里的调用
现在用正确的方式调用成员函数:
#include "File.h" bool function(Category* data) { if (data) { int action = 1; // 创建一个pQueue对象 pQueue my_queue; // 通过对象调用queueWork,传入任务参数 my_queue.queueWork(action, data); // TODO: 实现获取任务结果的逻辑 } return true; }
再回头说那个拷贝构造函数错误
现在你知道了:因为pQueue里有std::thread成员,线程是不能复制的,所以C++自动删除了默认的拷贝构造函数。之前你错误地把pQueue对象当参数传,相当于要复制这个对象,自然就触发了错误。现在我们的代码里不会再尝试复制pQueue对象,这个错误也就消失了。
额外提醒
- 多线程操作队列一定要用互斥锁,不然会出现“竞态条件”,程序可能崩溃或者数据乱掉。
- 如果想优化线程空转的问题,可以用
std::condition_variable,让线程在队列为空时休眠,有新任务再唤醒,这样更省CPU。 - 注意
Category*的内存管理,用完要记得释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Friday

