想使用队列处理一些多线程变量传递问题, 过程中发现有些写法是可以优化的,这里记录一下队列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() 语句调用有参构造函数,之后再析构, 认为这也是一个效率低下的行为.
总结
个人喜欢测试代码一的构建方式