QTableWidget 中insertRow(0)(头插)和 insertRow(rowCount())(尾插)的性能差异

news2025/4/13 5:56:31

一、目的

在 Qt 的 QTableWidget 中,insertRow(0) (头插)和 insertRow(rowCount())(尾插)在性能上存在显著差异。


二、QAbstractItemModel:: insertRows 原文解释

QAbstractItemModel Class | Qt Core 5.15.18

![[QAbstractItemModel-insertRows.png]]

AI 解释

QAbstractItemModel:: insertRows 是 Qt 模型/视图框架中用于在模型中插入行的一个虚函数。以下是对该函数的详细解释,包括其功能、参数、返回值以及如何正确实现自定义模型中的插入操作。

函数声明

[virtual] bool QAbstractItemModel::insertRows(int row, int count, const QModelIndex &parent = QModelIndex())

功能概述

insertRows 函数用于在模型中指定位置插入一行或多行数据。具体来说,它会在给定的 row 位置之前插入 count 行新数据。这些新行将成为由 parent 模型索引表示的父项的子项。

参数说明

  • int row
    指定插入行的位置。如果 row0,新行将被插入到父项的最前面;如果 row 等于 rowCount(parent),新行将被追加到父项的最后面。
  • int count
    要插入的行数。可以一次性插入多行数据。
  • const QModelIndex &parent = QModelIndex()
    表示插入行的父索引。如果 parent 是无效索引(默认值),则插入的行将成为顶级行(即没有父项)。否则,新行将作为 parent 所表示的父项的子项插入。

返回值

  • bool
    返回 true 表示插入操作成功;返回 false 表示插入操作失败。

注意事项

  1. 基类实现
    QAbstractItemModel 的基类实现是空的,直接调用会返回 false。因此,如果你的自定义模型需要支持插入行的操作,必须重写(reimplement)此函数。
  2. 通知机制
    在实际插入数据之前和之后,必须调用 beginInsertRowsendInsertRows 函数。这两个函数用于通知视图和其他相关组件模型数据的变化,以确保界面能够正确更新。
  3. 实现逻辑
    当重写 insertRows 时,除了调用 beginInsertRowsendInsertRows 来通知变化外,还需要在内部数据结构中实际插入新的数据行。

示例代码

以下是一个简单的自定义模型中重写 insertRows 的示例:

#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QList>

class MyModel : public QAbstractItemModel {
    Q_OBJECT

public:
    // 构造函数和其他必要的成员函数...

    // 重写 insertRows 函数
    bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override {
        // 开始插入操作的通知
        beginInsertRows(parent, row, row + count - 1);

        // 在内部数据结构中插入新行
        if (!parent.isValid()) {
            // 插入顶级行
            for (int i = 0; i < count; ++i) {
                m_data.insert(row, QVariant()); // 假设 m_data 是 QList<QVariant>
            }
        } else {
            // 插入子行(假设父项有子项列表)
            MyItem *parentItem = static_cast<MyItem*>(parent.internalPointer());
            if (parentItem) {
                parentItem->appendChildItems(count); // 假设有相应的方法添加子项
            }
        }

        // 结束插入操作的通知
        endInsertRows();

        return true; // 插入成功
    }

    // 其他必要的虚函数实现...

private:
    QList<QVariant> m_data; // 示例数据结构,实际应用中可能更复杂
};

详细解释

  1. 调用 beginInsertRows

    beginInsertRows(parent, row, row + count - 1);
    

    这个函数通知所有连接到模型的视图,即将在 parent 父索引下的 rowrow + count - 1 位置插入新行。这使视图能够预先调整其布局和缓冲区,以适应即将到来的数据变化。

  2. 修改内部数据结构
    根据 parent 是否有效,决定是插入顶级行还是子行:

    • 顶级行:直接在 m_data 列表的指定位置插入新数据。
    • 子行:通过 parent 获取对应的父项对象,并调用相应的方法在父项下插入新的子项。
  3. 调用 endInsertRows

    endInsertRows();
    

    这个函数通知视图插入操作已完成,视图可以更新显示新的数据行。

  4. 返回值
    返回 true 表示插入操作成功。如果插入过程中出现错误,可以返回 false,但通常需要提供适当的错误处理机制。

使用场景

  • 动态添加数据:当用户通过界面按钮或其他方式请求添加新行时,模型通过 insertRows 插入新数据。
  • 批量插入:一次性插入多行数据,提高效率。
  • 层级结构:在树形结构模型中,为特定父项插入子行。

性能考虑

  • 通知开销:频繁调用 beginInsertRowsendInsertRows 可能导致性能下降。因此,应尽量批量插入数据,而不是逐行插入。
  • 数据结构的效率:确保内部数据结构支持高效的插入操作。例如,使用链表可以快速在头部插入,而动态数组在尾部插入更高效。

总结

QAbstractItemModel::insertRows 是实现自定义模型时用于插入新行的关键函数。通过正确重写此函数,并结合 beginInsertRowsendInsertRows 的通知机制,可以确保模型数据的动态变化能够正确反映在关联的视图上,同时保持良好的性能和用户体验。


三、QTableWidget::insertRow(int row) 源码

路径

  • Qt\5.15.2\Src\qtbase\src\widgets\itemviews\qtablewidget. cpp
  • Qt\5.15.2\Src\qtbase\src\widgets\itemviews\qtablewidget_p.h

代码解析

void QTableWidget::insertRow(int row)
{
    Q_D(QTableWidget);
    d->tableModel()->insertRows(row);
}

bool QTableModel::insertRows(int row, int count, const QModelIndex &)
{
    if (count < 1 || row < 0 || row > verticalHeaderItems.count())
        return false;

    beginInsertRows(QModelIndex(), row, row + count - 1);
    int rc = verticalHeaderItems.count();
    int cc = horizontalHeaderItems.count();
    verticalHeaderItems.insert(row, count, 0);
    if (rc == 0)
        tableItems.resize(cc * count);
    else
        tableItems.insert(tableIndex(row, 0), cc * count, 0);
    endInsertRows();
    return true;
}

class QTableModel : public QAbstractTableModel
{
	Q_OBJECT
	friend class QTableWidget;
	.....
private:
	const QTableWidgetItem *prototype;
	QVector<QTableWidgetItem*> tableItems;
	QVector<QTableWidgetItem*> verticalHeaderItems;
	QVector<QTableWidgetItem*> horizontalHeaderItems;
	// A cache must be mutable if get-functions should have const modifiers
	mutable QModelIndexList cachedIndexes; };

从这段代码可以看出,QTableWidget 的插入行操作实际上是通过其内部的 QTableModel 来实现的,具体步骤包括:

  1. 插入表头项verticalHeaderItems.insert(row, count, 0);
  2. 插入表格数据项
    • 如果当前表格没有行 (rc == 0),则直接调整 tableItems 的大小。
    • 否则,在指定位置插入新的数据项,tableItems.insert(tableIndex(row, 0), cc * count, 0);

性能差异分析

1. 性能差异的核心原因

头插 (insertRow(0))
  • 表头项操作
    verticalHeaderItems.insert(row, count, 0)QVector 头部插入元素,需要将后续所有元素向后移动,时间复杂度为 ​O (n)(n 为当前行数)
  • 表格数据操作
    tableItems.insert(tableIndex(row, 0), cc * count, 0) 在数据数组头部插入新元素,同样需要移动后续所有数据,时间复杂度为 ​O (n * m)(m 为列数)
  • 视图更新
    触发 beginInsertRowsendInsertRows,通知视图重新计算所有行的位置,导致界面重绘开销较大。
尾插 (insertRow(rowCount()))
  • 表头项操作
    QVector 尾部追加元素,时间复杂度为 ​O (1)(假设预分配了足够内存)
  • 表格数据操作
    • 若表格为空,通过 tableItems.resize(cc * count) 初始化内存(O (1))。
    • 若表格非空,直接追加到 QVector 末尾(O (1))。
  • 视图更新
    视图仅需扩展显示区域,渲染开销更低。

2. 插入操作的时间复杂度

  • 尾插 (insertRow(rowCount()))
    • 表头项插入:在 QVector 的末尾插入元素,时间复杂度为 ​O(1)
    • 表项数据插入:如果表格为空,仅需调整大小;否则,由于是在末尾插入,QVector::insert 在有预留空间的情况下也是 ​O(1)。但如果有重新分配内存的需求,可能会涉及到 ​O(n) 的复制操作,但这种情况在尾部插入时较少发生。
  • 头插 (insertRow(0))
    • 表头项插入:在 QVector 的开头插入元素,需要移动所有现有元素,时间复杂度为 ​O(n)
    • 表项数据插入:同样,在开头插入需要移动所有现有的数据项,时间复杂度为 ​O(n)。如果表格较大,这种移动操作的开销会显著增加。

3. 实际性能影响

  • 数据量较小
    • 当表格中的行数较少(例如几十行)时,头插和尾插的性能差异可能不明显,用户几乎感觉不到延迟。
  • 数据量较大
    • 当表格包含数千行甚至更多行时,头插操作由于需要频繁移动大量元素,会导致明显的性能下降,甚至可能造成界面卡顿或响应延迟。
    • 尾插操作由于主要在末尾添加元素,性能相对稳定,几乎不受插入次数的影响。

4. 内存与缓存的影响

  • 内存重新分配
    • QVector 在插入元素时,如果当前容量不足以容纳新元素,会进行内存重新分配和元素复制。头插操作由于频繁移动元素,可能更频繁地触发内存重新分配,增加开销。
  • 缓存局部性
    • 尾插操作更有利于 CPU 缓存的利用,因为新元素通常被添加到内存的连续区域。而头插操作打乱了数据的连续性,导致缓存命中率降低,进一步影响性能。

5. 实际性能对比

操作类型时间复杂度内存移动次数适用场景性能影响
头插O(n)高(n 次移动)按倒序插入少量数据高(避免频繁使用)
尾插O(1)低(尾部追加)常规数据追加、大规模插入低(推荐优先使用)
  • 小数据量场景​(如数十行):两者差异可忽略。
  • 大数据量场景​(如数万行): - 头插单行耗时可能是尾插的 ​10 倍以上​(因需移动全部数据)。 - 尾插性能稳定,适合高频插入操作。

四、​ 示例测试

以下是一个使用 QElapsedTimerQTableWidget 测试 insertRow(0)(头插)和 insertRow(rowCount())(尾插)性能差异的完整示例代码。代码通过批量插入数据并测量时间,直观展示两者的性能差距:

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QElapsedTimer>
#include <QTimer>
#include <QDebug>

// 测试函数:插入指定行数到表格的头部或尾部
void testInsertPerformance(QTableWidget* table, int rowCount, const QString& testName) {
	table->clearContents();
	table->setRowCount(0);

	// 禁用视图更新以提高测试准确性
	table->setUpdatesEnabled(false);

	QElapsedTimer timer;
	timer.start();

	for (int i = 0; i < rowCount; ++i) {
		int row = (testName == "Head Insert") ? 0 : table->rowCount();
		table->insertRow(row);
		table->setItem(row, 0, new QTableWidgetItem(QString("Name %1").arg(i)));
		table->setItem(row, 1, new QTableWidgetItem(QString::number(i)));
		table->setItem(row, 2, new QTableWidgetItem(QString("City %1").arg(i % 10)));
	}

	auto elapsed_ms = timer.elapsed();
	table->setUpdatesEnabled(true); // 恢复视图更新

	qDebug() << testName << "Performance:";
	qDebug() << "  Time elapsed:" << elapsed_ms << "ms";
	qDebug() << "  Time elapsed:" << elapsed_ms << "ms";
	qDebug() << "  Memory used:" << table->sizeHint().height() * sizeof(QTableWidgetItem*) / 1024.<< "KB";
	qDebug() << "  Memory used:" << table->sizeHint().width() * sizeof(QTableWidgetItem*) / 1024.<< "KB";
}

int main(int argc, char* argv[]) {
	QApplication a(argc, argv);

	// 创建测试表格(3列)
	QTableWidget table;
	table.setColumnCount(3);
	table.setHorizontalHeaderLabels({ "Name", "Age", "City" });
	table.resize(600, 400);
	table.show();
#if 1
	// 测试头插性能(插入100000行)
	testInsertPerformance(&table, 100000, "Head Insert");

	// 等待用户操作后测试尾插性能
	QTimer::singleShot(2000, [&]() {
		testInsertPerformance(&table, 100000, "Tail Insert");
		});
#else
	//测试尾插性能
	testInsertPerformance(&table, 100000, "Tail Insert");
	// 等待用户操作后测试头插性能
	QTimer::singleShot(2000, [&]() {
		testInsertPerformance(&table, 100000, "Head Insert");
		});
#endif
	return a.exec();
}

代码解析与测试结果

1. ​核心逻辑
  • 禁用视图更新:通过 setUpdatesEnabled(false) 暂停界面刷新,避免渲染开销干扰时间测量。
  • 批量插入数据:循环插入指定行数,每行包含姓名、年龄、城市三列数据。
  • 时间测量:使用 QElapsedTimer 记录插入操作的耗时。
  • 内存估算:通过 sizeHint().height() 估算表格占用的内存(粗略计算)。
2. ​测试结果示例

![[100000performance.png]]

操作类型插入行数耗时(ms)​内存占用(KB)​
头插100,0009,2241.5
尾插100,00025091.5

:实际结果可能因硬件和 Qt 版本略有差异,但头插耗时通常比尾插高 ​。


性能差异原因

  1. 头插 (insertRow(0))

    • 数据移动QVector 在头部插入元素需移动后续所有元素,时间复杂度为 ​O(n)
    • 内存重新分配:频繁插入导致内存多次重新分配,增加开销。
  2. 尾插 (insertRow(rowCount()))

    • 尾部追加QVector 在尾部插入元素时间复杂度为 ​O(1)(预分配内存时)
    • 内存连续性:数据连续存储,缓存命中率高。

优化建议

  1. 预分配内存:若已知数据量,提前调用 table->setRowCount(rowCount) 预分配内存。
  2. 改用自定义模型:对超大数据集,使用 QAbstractTableModel 替代 QTableWidget,通过虚拟化技术减少内存占用。

五、​ 优化建议

基于上述分析,以下是一些优化建议:

  1. 优先使用尾插

    • 如果业务逻辑允许,尽量使用 insertRow(rowCount()) 进行尾插操作,以获得更好的性能表现。
  2. 批量插入

    • 如果需要插入多行,尽量一次性批量插入,而不是逐行插入。例如,先收集所有需要插入的数据,然后调用一次 setRowCount
    // 示例:批量插入多行
    tableWidget->setRowCount(10000);
    for (int i = 0; i < 10000; ++i) {
        // 填充数据
    }
    
  3. 使用模型/视图架构

    • 如果需要频繁进行插入、删除等操作,考虑直接使用 QAbstractTableModel 或其子类 QStandardItemModel,而不是 QTableWidgetQAbstractTableModel 提供了更高的灵活性和性能优化空间,尤其是在处理大规模数据时。
    • 对于超大数据集,推荐使用 QTableView + 自定义 QAbstractTableModel / QStandardItemModel,通过虚拟化技术减少内存和渲染开销。例如:
    class CustomTableModel : public QAbstractTableModel {
        // 实现 data()、rowCount()、columnCount() 等虚函数
    };
    QTableView *tableView = new QTableView;
    tableView->setModel(new CustomTableModel);
    // 或者
    tableView->setModel(new QStandardItemModel);
    
  4. 延迟更新

    • 若必须头插,可先禁用视图更新(setUpdatesEnabled(false)),插入多行后再统一刷新界面:
    tableWidget->setUpdatesEnabled(false);
    for (int i = 0; i < 100; ++i) {
        tableWidget->insertRow(0); // 批量头插
    }
    tableWidget->setUpdatesEnabled(true);
    
    • 在进行大量插入操作前,可以暂时禁用视图的更新,操作完成后再恢复。这可以通过 setUpdatesEnabled(false)setUpdatesEnabled(true) 实现,但需要注意处理好数据的一致性。
    tableWidget->setUpdatesEnabled(false);
    // 执行批量插入操作
    tableWidget->setUpdatesEnabled(true);
    
  5. 懒加载技术

    • 仅在可见区域加载数据,结合滚动事件动态插入行(参考 QT 懒加载技术 的 UpdateAlarmList 实现):
    void AlarmCenter::wheelEvent(QWheelEvent *event) {
        // 根据滚动条位置动态加载数据
        int visibleRows = tableViewHeight / rowHeight;
        int startRow = currentRow - visibleRows / 2;
        UpdateAlarmList(startRow, visibleRows);
    }
    
  6. 性能对比总结

指标头插尾插
时间复杂度O (n)(数据移动)O (1)(尾部追加)
内存操作高(频繁重新分配和复制)低(预分配或追加)
界面渲染高(全表重绘)低(仅扩展区域)
适用场景倒序插入少量数据常规追加、大规模插入

总结

  • 头插 (insertRow(0))
    • 性能较低,尤其是在数据量较大时,由于需要在开头插入元素,导致所有现有元素需要移动,时间复杂度为 ​O(n)
  • 尾插 (insertRow(rowCount()))
    • 性能较高,在末尾插入元素通常为 ​O(1),即使有内存重新分配,也相对高效。

因此,在使用 QTableWidget::insertRow 时,​尾插性能远优于头插,应优先考虑尾插操作,以获得更好的性能表现。如果业务逻辑确实需要频繁进行头插操作,建议重新评估设计,或者考虑使用更适合频繁插入删除操作的模型/视图架构(如 QAbstractTableModel)配合自定义的数据结构优化性能。​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2328183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【万字总结】前端全方位性能优化指南(完结篇)——自适应优化系统、遗传算法调参、Service Worker智能降级方案

前言 自适应进化宣言 当监控网络精准定位病灶&#xff0c;真正的挑战浮出水面&#xff1a;系统能否像生物般自主进化&#xff1f; 五维感知——通过设备传感器实时捕获环境指纹&#xff08;如地铁隧道弱光环境自动切换省电渲染&#xff09; 基因调参——150个性能参数在遗传算…

不绕弯地解决文件编码问题,锟斤拷烫烫烫

安装python对应库 pip install chardet 检测文件编码 import chardet# 检测文件编码 file_path rC:\Users\AA\Desktop\log.log # 这里放文件和文件绝对路径 with open(file_path, rb) as f:raw_data f.read(100000) # 读取前10000个字节result chardet.detect(raw_data)e…

高密度任务下的挑战与破局:数字样机助力火箭发射提效提质

2025年4月1日12时&#xff0c;在酒泉卫星发射中心&#xff0c;长征二号丁运载火箭顺利升空&#xff0c;成功将一颗卫星互联网技术试验卫星送入预定轨道&#xff0c;发射任务圆满完成。这是长征二号丁火箭的第97次发射&#xff0c;也是长征系列火箭的第567次发射。 执行本次任务…

QT Quick(C++)跨平台应用程序项目实战教程 6 — 弹出框

目录 1. Popup组件介绍 2. 使用 上一章内容完成了音乐播放器程序的基本界面框架设计。本小节完成一个简单的功能。单击该播放器顶部菜单栏的“关于”按钮&#xff0c;弹出该程序的相关版本信息。我们将使用Qt Quick的Popup组件来实现。 1. Popup组件介绍 Qt 中的 Popup 组件…

KisFlow-Golang流式实时计算案例(四)-KisFlow在消息队列MQ中的应用

Golang框架实战-KisFlow流式计算框架专栏 Golang框架实战-KisFlow流式计算框架(1)-概述 Golang框架实战-KisFlow流式计算框架(2)-项目构建/基础模块-(上) Golang框架实战-KisFlow流式计算框架(3)-项目构建/基础模块-(下) Golang框架实战-KisFlow流式计算框架(4)-数据流 Golang框…

leetcode:1582. 二进制矩阵中的特殊位置(python3解法)

难度&#xff1a;简单 给定一个 m x n 的二进制矩阵 mat&#xff0c;返回矩阵 mat 中特殊位置的数量。 如果位置 (i, j) 满足 mat[i][j] 1 并且行 i 与列 j 中的所有其他元素都是 0&#xff08;行和列的下标从 0 开始计数&#xff09;&#xff0c;那么它被称为 特殊 位置。 示…

Cline – OpenRouter 排名第一的CLI 和 编辑器 的 AI 助手

Cline – OpenRouter 排名第一的CLI 和 编辑器 的 AI 助手&#xff0c;Cline 官网&#xff1a;https://github.com/cline/cline Star 37.8k ps&#xff0c;OpenRouter的网址是&#xff1a;OpenRouter &#xff0c;这个排名第一&#xff0c;据我观察&#xff0c;是DeepSeek v3…

Mock.js虚拟接口

Vue3中使用Mock.js虚拟接口数据 一、创建项目 pnpm创建vite的项目,通过 PNPM来简化依赖管理。若还没有安装 PNPM&#xff0c;可以通过 npm来安装&#xff1a; 安装 PNPM npm install -g pnpm//使用国内镜像加速pnpm add -g pnpmlatestpnpm config set registry http://regis…

LoRa模块通信距离优化:如何实现低功耗覆盖30公里无线传输要求

在物联网&#xff08;IoT&#xff09;快速发展的今天&#xff0c;LoRa&#xff08;Long Range&#xff09;技术作为一种基于扩频调制的远距离无线通信技术&#xff0c;因其远距离通信、低功耗和强抗干扰能力等优势&#xff0c;在农业监测、城市智能管理、环境监测等多个领域得到…

OpenCV 从入门到精通(day_05)

1. 模板匹配 1.1 什么是模板匹配 模板匹配就是用模板图&#xff08;通常是一个小图&#xff09;在目标图像&#xff08;通常是一个比模板图大的图片&#xff09;中不断的滑动比较&#xff0c;通过某种比较方法来判断是否匹配成功。 1.2 匹配方法 rescv2.matchTemplate(image, …

OpenRouter开源的AI大模型路由工具,统一API调用

简介 ‌OpenRouter是一个开源的路由工具‌&#xff0c;它可以绕过限制调用GPT、Claude等国外模型。以下是对它的详细介绍&#xff1a; 一、主要功能 OpenRouter专注于将用户请求智能路由到不同的AI模型&#xff0c;并提供统一的访问接口。它就像一个“路由器”&#xff0c;能…

zabbix监控网站(nginx、redis、mysql)

目录 前提准备&#xff1a; zabbix-server主机配置&#xff1a; 1. 安装数据库 nginx主机配置&#xff1a; 1. 安装nginx redis主机配置&#xff1a; 1. 安装redis mysql主机配置&#xff1a; 1. 安装数据库 zabbix-server&#xff1a; 1. 安装zabbix 2. 编辑配置文…

蓝桥杯冲刺

例题1&#xff1a;握手问题 方法1&#xff1a;数学推理(简单粗暴&#xff09; 方法2&#xff1a;用代码实现方法1 #include<iostream> using namespace std; int main() {int result 0;for (int i 1; i < 49; i){for (int j i 1; j < 50; j){//第i个人与第j个…

Spring Security(maven项目) 3.1.0

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…

C# 从代码创建选型卡+表格

private int tabNum 1; private int sensorNum 5; private void InitializeUI() {// 创建右侧容器面板Panel rightPanel new Panel{Dock DockStyle.Right,Width 300,BackColor SystemColors.ControlDark,Parent this};// 根据防区数量创建内容if (tabNum &g…

OpenCV 从入门到精通(day_02)

1. 边缘填充 为什么要填充边缘呢&#xff1f;我们以下图为例&#xff1a; 可以看到&#xff0c;左图在逆时针旋转45度之后原图的四个顶点在右图中已经看不到了&#xff0c;同时&#xff0c;右图的四个顶点区域其实是什么都没有的&#xff0c;因此我们需要对空出来的区域进行一个…

Ceph异地数据同步之-RBD异地同步复制(上)

#作者&#xff1a;闫乾苓 文章目录 前言基于快照的模式&#xff08;Snapshot-based Mode&#xff09;工作原理单向同步配置步骤单向同步复制测试双向同步配置步骤双向同步复制测试 前言 Ceph的RBD&#xff08;RADOS Block Device&#xff09;支持在两个Ceph集群之间进行异步镜…

【C++】STL库_stack_queue 的模拟实现

栈&#xff08;Stack&#xff09;、队列&#xff08;Queue&#xff09;是C STL中的经典容器适配器 容器适配器特性 不是独立容器&#xff0c;依赖底层容器&#xff08;deque/vector/list&#xff09;通过限制基础容器接口实现特定访问模式不支持迭代器操作&#xff08;无法遍历…

一周学会Pandas2 Python数据处理与分析-编写Pandas2 HelloWord项目

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们首先准备一个excel文件&#xff0c;用来演示pandas操作数据集(数据的集合)。excel文件属于数据集的一种&#xf…

【易订货-注册/登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…