探讨C++ std::queue队列涉及的拷贝问题

2023-05-16

想使用队列处理一些多线程变量传递问题, 过程中发现有些写法是可以优化的,这里记录一下队列push,pop过程中的拷贝问题

测试代码一如下:

#include <iostream>
#include <queue>

struct Job {
    int a{};
    float *b = new float;
    std::string c;

    Job() { printf("无参构造函数\n"); }

    Job(const Job &job) {
        printf("有参构造函数\n");
        this->a = job.a;
        this->b = job.b;
    };

    ~Job() { printf("析构函数\n"); }
};

std::queue<Job> qJobs;

int main() {
    Job job;
//    printf("%p\n",&job);
    printf("==========================\n");
    for (int i = 0; i < 2; ++i) {
//        Job job;
        printf("%p\n", &job);
        job.a = i + 1;
        job.b = (float *) malloc(sizeof(float) * 1);
        job.b[0] = float(i + 10);
//        qJobs.emplace(std::move(job));
        qJobs.push(job);
    }
    printf("--------------------------\n");
    Job job1;
    printf("job1变量地址: %p\n", &job1);
    while (!qJobs.empty()) {
        job1 = qJobs.front();
        qJobs.pop();
        printf("%p, %d, %f\n", &job1, job1.a, *job1.b);
    }
    printf("++++++++++++++++++++++++++++\n");
}

上述代码中, 向队列写入结构体元素, 之后再取出来打印,结果如下:

无参构造函数
==========================
0x7ffef349c110
有参构造函数
0x7ffef349c110
有参构造函数
--------------------------
无参构造函数
job1变量地址: 0x7ffef349c140
析构函数
0x7ffef349c140, 1, 10.000000
析构函数
0x7ffef349c140, 2, 11.000000
++++++++++++++++++++++++++++
析构函数
析构函数

进程已结束,退出代码0

可知队列的push操作会调用结构体或类的有参拷贝构造函数. 而每pop一个元素,也会调用该元素的析构函数.
而打印出来的job 和 job1 地址不同, 是因这个结构体变量都开辟在栈上, 创建job1时, job还没析构,所以会另开辟一块空间.
从最后打印结果也可知, job,job1两个变量是在程序结束时析构的. 测试代码二给出这点证明.

测试代码二如下:

int main() {
    { // 加作用域
        Job job;
//    printf("%p\n",&job);
        printf("==========================\n");
        for (int i = 0; i < 2; ++i) {
//        Job job;
            printf("%p\n", &job);
            job.a = i + 1;
            job.b = (float *) malloc(sizeof(float) * 1);
            job.b[0] = float(i + 10);
//        qJobs.emplace(std::move(job));
            qJobs.push(job);
        }
    }
    printf("--------------------------\n");
    Job job1;
    printf("job1变量地址: %p\n", &job1);
    while (!qJobs.empty()) {
        job1 = qJobs.front();
        qJobs.pop();
        printf("%p, %d, %f\n", &job1, job1.a, *job1.b);
    }
    printf("++++++++++++++++++++++++++++\n");
}
无参构造函数
==========================
0x7ffd0d5c6190
有参构造函数
0x7ffd0d5c6190
有参构造函数
析构函数
--------------------------
无参构造函数
job1变量地址: 0x7ffd0d5c6190
析构函数
0x7ffd0d5c6190, 1, 10.000000
析构函数
0x7ffd0d5c6190, 2, 11.000000
++++++++++++++++++++++++++++
析构函数

进程已结束,退出代码0

在上述测试二中,把job的生命周期限制在一个作用域中,在作用域结束后变量job释放空间, 那么创建job1会在已释放空间上创建, 注意测试一与测试二中地址的变化.

测试代码三如下:

那么,如果把job对象创建在for循环中,又会有什么不同呢?

int main() {
//    Job job;
//    printf("%p\n",&job);
    printf("==========================\n");
    for (int i = 0; i < 2; ++i) {
        Job job;
        printf("%p\n", &job);
        job.a = i + 1;
        job.b = (float *) malloc(sizeof(float) * 1);
        job.b[0] = float(i + 10);
//        qJobs.emplace(std::move(job));
        qJobs.push(job);
    }
    printf("--------------------------\n");
    Job job1;
    printf("job1变量地址: %p\n", &job1);
    while (!qJobs.empty()) {
        job1 = qJobs.front();
        qJobs.pop();
        printf("%p, %d, %f\n", &job1, job1.a, *job1.b);
    }
    printf("++++++++++++++++++++++++++++\n");
}
==========================
无参构造函数
0x7fff484f3740
有参构造函数
析构函数
无参构造函数
0x7fff484f3740
有参构造函数
析构函数
--------------------------
无参构造函数
job1变量地址: 0x7fff484f3740
析构函数
0x7fff484f3740, 1, 10.000000
析构函数
0x7fff484f3740, 2, 11.000000
++++++++++++++++++++++++++++
析构函数

进程已结束,退出代码0

结果表明, job每次都会创建新的, 拷贝到队列时会调用拷贝构造函数, 一次for循环结束时会析构掉. 每次for循环都是如此, 认为这样的效率比较低下. 同时, job的生命周期只在for中,因此pop时的job1 变量地址会与job相同.

测试代码四如下:

取出元素pop操作时, 每次新建变量效率如何呢?

int main() {
//    Job job;
//    printf("%p\n",&job);
    printf("==========================\n");
    for (int i = 0; i < 2; ++i) {
        Job job;
        printf("%p\n", &job);
        job.a = i + 1;
        job.b = (float *) malloc(sizeof(float) * 1);
        job.b[0] = float(i + 10);
//        qJobs.emplace(std::move(job));
        qJobs.push(job);
    }
    printf("--------------------------\n");
//    Job job1;
//    printf("job1变量地址: %p\n", &job1);
    while (!qJobs.empty()) {
        Job job1 = qJobs.front();
        qJobs.pop();
        printf("%p, %d, %f\n", &job1, job1.a, *job1.b);
    }
    printf("++++++++++++++++++++++++++++\n");
}
==========================
无参构造函数
0x7ffd01ed4a10
有参构造函数
析构函数
无参构造函数
0x7ffd01ed4a10
有参构造函数
析构函数
--------------------------
有参构造函数
析构函数
0x7ffd01ed4a10, 1, 10.000000
析构函数
有参构造函数
析构函数
0x7ffd01ed4a10, 2, 11.000000
析构函数
++++++++++++++++++++++++++++

进程已结束,退出代码0

可见,在pop时, 会在Job job1 = qJobs.front() 语句调用有参构造函数,之后再析构, 认为这也是一个效率低下的行为.

总结
个人喜欢测试代码一的构建方式

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

探讨C++ std::queue队列涉及的拷贝问题 的相关文章

  • 包中涉及std::transform的Rcpp代码的兼容性

    我正在完成我一直在做的一个包 所有检查看起来都很好 并且在我的计算机上编译没有问题 win builder包裹也没有问题 作为进一步检查 我尝试在同事的计算机上从源代码安装 但失败了 问题来自于我从中获取的 Rcpp 函数Rcpp 中关于向
  • Node.js 公牛队列中的作业陷入“等待”状态

    我有一堆工作在公牛队列中 其中一个被卡住了 1 个多小时 通常需要大约 2 分钟才能运行 但没有失败 我无法使用我使用的 bull arena UI 将作业从活动状态中删除 因此我删除了 Redis 中活动作业的密钥 这消除了卡住的活动作业
  • 如何在没有线程或任务队列的情况下在 Flask 中运行后台作业

    我正在使用 Flask restplus 构建 REST API 我的端点之一获取从客户端上传的文件并运行一些分析 该作业最多需要 30 秒 我不希望这项工作阻塞主进程 因此端点将立即返回 200 或 201 响应 作业仍然可以运行 结果将
  • C++ TR2 文件系统库的状态如何?

    截至上次布里斯托尔会议 C TR2 文件系统库的状态如何 它将成为 C 1Y C 14 的一部分 还是暂停 或者最近三次会议有任何已知的评论吗 It has 最近获得ISO委员会一致批准 http article gmane org gma
  • SQL Server 进程队列竞争条件

    我有一个订单队列 多个订单处理器通过存储过程访问该队列 每个处理器都会传递一个唯一的 ID 该 ID 用于锁定接下来的 20 个订单以供自己使用 然后 存储过程将这些记录返回给订单处理器以进行操作 有些情况下多个处理器能够检索相同的 Ord
  • C++中的地图数据结构是什么[重复]

    这个问题在这里已经有答案了 C 中以下代码行使用的数据结构是什么 map
  • ios::noreplace 的 C++ 替换

    我正在使用 fstream 打开文件进行写入 我不想覆盖现有文件 因此经过一番搜索后 我发现了 ios noreplace 但是当我编译这个时 include
  • 使用 Javascript 进行速率限制并将 ajax 调用排队为每 15 秒一次

    我有一个应用程序 每次用户执行某些操作时都会自动发送推文 如果用户愿意 可以轻松地每秒执行一次该操作 Twitter 的速率限制表示 它关注 15 分钟内发生了多少条推文 从技术上讲 我认为我总是低于 15 分钟标记 但 Twitter 似
  • 如何限制创建 celery 任务的速度快于消耗速度的脚本?

    我有一个脚本可以生成数百万个 Celery 任务 数据库中每行一个任务 有没有办法限制它 以免它完全淹没芹菜 理想情况下 我想让 Celery 保持忙碌 但我不希望 Celery 队列的长度超过几十个任务 因为这只是浪费内存 特别是因为如果
  • GCC 4.7.2:带有指向成员函数指针的 std::thread

    在编写测试代码时这个问题 https stackoverflow com questions 15080015 stdthread with pointer to data member我发现下面的注释行无法在 GCC 4 7 2 上编译
  • 队列对象只能通过继承在进程之间共享

    我有两个 python 类 它们共享一个基类 可以说它定义了一个 multiprocessing Queue 每个类都将在单独的子进程上启动 并且需要通过队列共享信息 现在 如果基类定义了一个队列 那么每个子类对象将实例化它自己的队列 从而
  • 使用一个或多个标准 FIFO 队列实现延迟队列 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 延迟队列是一种队列 其中每条消息都有
  • Azure 有害队列计数警报规则

    在之前的一个项目中 我设法设置了一个警报规则 该规则会查看有害队列消息计数 并在队列中存在某些内容时 每天一次 使用 webhook 向 slack 发出警报 我试图找到它在 Azure 中的位置 因为看起来事情已经发生了变化 如果这不是
  • $(this).dequeue();与下一个();

    如果我这样做有什么区别吗 queue queue function next next queue function next next versus queue queue function this dequeue queue func
  • GCD获取队列名称/标签

    如何获取当前队列名称 我的意思是队列标签com example myqueue 在 Xcode 4 调试器中我只能看到 block invoke 1 怎么样dispatch queue get label http developer ap
  • 使用 std::set 时重载运算符<

    这是我第一次使用 std set 容器 并且我对操作符 std less 遇到了问题 我声明该集合 std set
  • Laravel:运行队列:在 Windows Azure Web App 上连续监听

    我觉得问这个问题有点傻 但我似乎无法在互联网上找到这个问题的答案 经过几个小时的搜索后 我发现在 Linux 服务器上 您使用 Supervisor 在您的网站上连续运行 php artisanqueue listen 无论有或没有守护进程
  • 删除队列中的最后一个元素

    我需要删除队列的最后一个元素 我唯一可以使用的操作是 Peek 获取第一个元素而不删除它 Enqueue element 向队列末尾插入一个元素 Dequeue 删除第一个元素 IsEmpty true 或 false 队列是否为空 而且我
  • 如何将标准库与C++模块一起使用? (例如:“导入 std.io”)

    中给出的基本示例如何在 Clang 中使用 C 模块 https stackoverflow com questions 33307657 how do i use c modules in clang对我有用 但不导入标准库 例如通过im
  • 从空缓冲区构造“std::ostream”是否有效?

    考虑以下 std ostream out nullptr 这是合法且明确定义的吗 如果我现在这样做怎么样 out lt lt hello world n 这是合法且明确定义的吗 如果是这样 大概这是一种无操作 是的 实例化该流是合法且定义明

随机推荐