(二):Qt信号槽连接及触发原理

2023-11-06

经过《Qt信号槽之—准备阶段》学习,我们知道信号是函数,里面调用了 QMetaObject::activate() 函数;
而对于接收端的槽函数或信号,是通过私有静态函数 qt_static_metacall() 调用元方法。
源头是有的,接收端也是有的,中间少了个桥,这个桥就是 Qt 元对象系统的技术内幕,我们需要阅读 Qt 核心源码才能知道。 后面三个小节就是把从源头到接收端的桥解析一下。

connect做了什么?

connect我们都知道是将信号和槽进行关联,具体这个桥梁如何搭建的,咱们下面进行分解。这里我们讲解Qt旧语法的信号槽,如下是一种咱们最常见的结构:

1.1 SIGNAL和SLOT宏

QMetaObject::Connection connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection)

实例:

bool flg = connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(slot_set_label_number()));

我们可以看到实例信号,和槽分别用两个宏进行处理,下面来看一下SIGNAL和SLOT宏分别代表什么:

# define SLOT(a)     qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)

把信号作为字符串前面加上了“2”,对于槽函数,前面加"1",因此,上面的实例等同下面这句

bool flg = connect(ui.pushButton, "2clicked()", this, "1slot_set_label_number()");

1.2 connect函数详解

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
                                     const QObject *receiver, const char *method,
                                     Qt::ConnectionType type)
{
    if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
        qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
                 sender ? sender->metaObject()->className() : "(null)",
                 (signal && *signal) ? signal+1 : "(null)",
                 receiver ? receiver->metaObject()->className() : "(null)",
                 (method && *method) ? method+1 : "(null)");
        return QMetaObject::Connection(0);
    }
    
    QByteArray tmp_signal_name;
	//用来检测是否使用SINGAL关键字,如果没有则报错,返回一个空Connection
    if (!check_signal_macro(sender, signal, "connect", "bind"))
        return QMetaObject::Connection(0);
    
    const QMetaObject *smeta = sender->metaObject();
    const char *signal_arg = signal;
    ++signal; //skip code  去掉前面的数字
    QArgumentTypeArray signalTypes;
    Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
    QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
    //计算相对序号
    int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
            &smeta, signalName, signalTypes.size(), signalTypes.constData());
    if (signal_index < 0) {
        //相对序号为负数,则表示没找到,则规范信号名(去除空格)
        // check for normalized signatures
        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
        signal = tmp_signal_name.constData() + 1;

        signalTypes.clear();
        signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
        smeta = sender->metaObject();
       
        //再次查找序号
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(
                &smeta, signalName, signalTypes.size(), signalTypes.constData());
    }
    if (signal_index < 0) {
        //两次都没找到,打印报警
        err_method_notfound(sender, signal_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QMetaObject::Connection(0);
    }
    //判断是否为克隆信号,如果是,则用原始序号
    signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
    //绝对序号
    signal_index += QMetaObjectPrivate::signalOffset(smeta);

    QByteArray tmp_method_name;
    int membcode = extract_code(method);

    if (!check_method_code(membcode, receiver, method, "connect"))
        return QMetaObject::Connection(0);
    const char *method_arg = method;
    ++method; // skip code

    QArgumentTypeArray methodTypes;
    QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
    const QMetaObject *rmeta = receiver->metaObject();
    int method_index_relative = -1;
    Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
    
    switch (membcode) {
    case QSLOT_CODE:
             //如果 method 是槽函数类型,使用 QMetaObjectPrivate::indexOfSlotRelative 函数计算槽函数相对序号 method_index_relative;
        method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
                &rmeta, methodName, methodTypes.size(), methodTypes.constData());
        break;
    case QSIGNAL_CODE:
            //如果 method 是信号类型,那么使用 QMetaObjectPrivate::indexOfSignalRelative 函数计算信号的相对序号 method_index_relative。
        method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
                &rmeta, methodName, methodTypes.size(), methodTypes.constData());
        break;
    }
    if (method_index_relative < 0) {
        // check for normalized methods
        //格式化
        tmp_method_name = QMetaObject::normalizedSignature(method);
        method = tmp_method_name.constData();

        methodTypes.clear();
        methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
        // rmeta may have been modified above
        rmeta = receiver->metaObject();
        switch (membcode) {
        case QSLOT_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
                    &rmeta, methodName, methodTypes.size(), methodTypes.constData());
            break;
        case QSIGNAL_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
                    &rmeta, methodName, methodTypes.size(), methodTypes.constData());
            break;
        }
    }
	//两次检测都失败,返回空链接
    if (method_index_relative < 0) {
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QMetaObject::Connection(0);
    }

    //检查信号函数参数个数、类型与接收函数的参数个数、类型是否 能兼容,如果不兼容返回空连接,如果兼容就继续往下走。
    if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
                                              methodTypes.size(), methodTypes.constData())) {
        qWarning("QObject::connect: Incompatible sender/receiver arguments"
                 "\n        %s::%s --> %s::%s",
                 sender->metaObject()->className(), signal,
                 receiver->metaObject()->className(), method);
        return QMetaObject::Connection(0);
    }

    int *types = 0;
    //如果连接类型 type == Qt::QueuedConnection,那么使用 queuedConnectionTypes 函数计算入队关联需要的额外类型指针 types
    if ((type == Qt::QueuedConnection)
            && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
        return QMetaObject::Connection(0);
    }

#ifndef QT_NO_DEBUG
    //QMetaObjectPrivate::signal 函数根据源头元对象smeta和信号相对序号 signal_index,得到信号元方法 smethod;
    QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
    //计算 method_index_relative + rmeta->methodOffset() ,也就是接收端的元方法绝对序号,然后通过 rmeta->method 函数得到接收端元方法 rmethod;
    QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
    //查源头元对象 smeta、元方法 smethod 、接收端元对象 rmeta、元方法 rmethod 是不是具有 QMetaMethod::Compatibility 特性。
    check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
    //)前面全都是参数判断和查询序号,最后才是关键的一步,进行实际的连接操作:
    QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
        sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
    return handle;
}

可以看到QObject内的connect函数前半部分都是在做类型检测,到最后获取到了几个信息

  • QObject sender 发送者
  • signal_index 信号绝对序号
  • QMetaObject smeta 发送者元对象
  • receiver 接收者
  • method_index_relative 接收者槽函数绝对序号
  • QMetaObject rmeta 接收者元对象
  • Qt::ConnectionType type 槽连接方式
  • types:是多线程程序的入队关联需要的额外类型指针
QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
                                 int signal_index, const QMetaObject *smeta,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    int method_offset = rmeta ? rmeta->methodOffset() : 0;
    Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
    QObjectPrivate::StaticMetaCallFunction callFunction =
        rmeta ? rmeta->d.static_metacall : 0;
//关联操作需要访问、修改源头和接收端的信号和槽信息,因此需要使用互斥锁,独占源头和接收端信号和槽信息的访问权,先锁定访问权,然后再进行操作。这对于多线程操 作尤为重要,不能多个线程同时操作一对源头和接收端,那样会造成信号和槽数据的混乱,如果情况严重会导致程序崩溃,因此启用互斥锁
    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));
    
//关联类型 type 是 Qt::AutoConnection、Qt::DirectConnection、Qt::QueuedConnection 或Qt::BlockingQueuedConnection,这些类型的关联,可以将完全相同的源头、信号、接收端、槽函数关联多次,那样信号触发一次,槽函数会被调用 多次,重复关联是有效的。Qt::UniqueConnection 是唯一关联的标志,可以与其他四种关联标志叠加(OR 运算),那样就会执行上面一段代码,检查关联的唯一性,如果是重复关联就返回 0 ,如果没重复就进行唯一性关联。上面代码就是穷举比较源头信号关联列表 c2 里面有没有重复的关联。一般关联类型 type 都是 Qt::AutoConnection,所以可以不用管上面一段唯一性检查的代码。
    if (type & Qt::UniqueConnection) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index) {
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;

            int method_index_absolute = method_index + method_offset;

            while (c2) {
                if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)
                    return 0;
                c2 = c2->nextConnectionList;
            }
        }
        type &= Qt::UniqueConnection - 1;
    }

    QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
    c->sender = s;//发送源头对象;
    c->signal_index = signal_index;//源头信号绝对序号
    c->receiver = r;//接收端对象;
    c->method_relative = method_index;//接收端方法相对序号;
    c->method_offset = method_offset;//接收端元方法偏移;
    c->connectionType = type;//连接类型;
    c->isSlotObject = false;//是否为 SlotObject,新式关联语法和 QML程序会用到这个奇怪的 SlotObject。
    c->argumentTypes.store(types);//保存多线程入队关联需要的 types 指针
    c->nextConnectionList = 0;//nextConnectionList 是下一个链表节点,暂时为 0,以后由 QObjectPrivate::addConnection 函数填充。
    c->callFunction = callFunction;//重点:接收端的私有静态函数 qt_static_metacall(),这个接收端私有静态函数是可以根据相对序号调用元方法的。

    QObjectPrivate::get(s)->addConnection(signal_index, c.data());

    locker.unlock();
    QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
    if (smethod.isValid())
        s->connectNotify(smethod);

    return c.take();
}

QObjectPrivate::addConnection 函数根据信号绝对序号 signal_index 和连接数据 c.data() 添加新的连接。
感兴趣的读者可以跟踪 QObjectPrivate::addConnection 函数源码,这里不贴代码了,一张数据结构图说明:
在这里插入图片描述

从信号到槽函数

信号触发时,调用moc_xx.cpp中的信号函数

void Sig_SLOT::label_number_change(int _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

其中调用复杂版本的版本

activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv);

这个函数比较复杂,只讲一下比较关键的部分

如果sender与receiver在一个线程,则直接使用如下函数回调槽函数

  callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

如果与sender与receiver不在同一个线程,使用如下函数构造QMetaCallEvent事件,然后Post到receiver的事件循环中。

    queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker)
    {
    ....
      QMetaCallEvent *ev = c->isSlotObject ?
        new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
        new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs, types, args);
    QCoreApplication::postEvent(c->receiver, ev);
    }

注意:QMetaCallEvent 是一个包含信号及槽相关信息,通过QMetaCallEvent ,接收对象可以在其所在线程中正确执行槽函数。

在这里插入图片描述

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

(二):Qt信号槽连接及触发原理 的相关文章

  • 如何在 Qt Creator 中编辑 QtWebKit 的右键上下文菜单?

    好吧 这是我的困境 我正在使用 Qt Creator 制作一个使用 Webkit 的简单应用程序 我认为 Qt Creator 会有一种简单的方法来使用信号和槽编辑器编辑右键单击上下文菜单 但事实证明这不是真的 我知道 webkit 有与上
  • 在 Qt 服务器上验证用户身份

    我正在尝试使用 C QtTcpSocket 为个人项目 多人国际象棋游戏 实现身份验证系统 我的朋友建议了一种验证用户的方法 但我想问是否有更简单或更好的方法 来自 Python 背景 做这个项目主要是为了加深对 C 的理解 我将发布我朋友
  • 在 QtCreator 中使用 .pro 和 .pri 正确(树)显示 Qt 项目

    是否可以使用项目包含文件 pri 显示不止一个额外级别 例如如果 pro文件包括 pri文件包含两个 pri files pro pri pri pri 在 QtCreator 项目 中显示具有误导性 pro pri pri pri 它根本
  • 在没有加载器的情况下实例化内联组件

    有没有办法实例化内联Component 即在同一文件中定义 而不使用Loader 我不太关心使用的性能影响Loader因为我要用很多东西污染我的文件Loader包装纸 我发现从 JavaScript 创建动态 QML 对象 http doc
  • QT:删除QGridLayout中QLabel之间的空格

    我将一些具有不同颜色的 QLabels 添加到 QGridLayout 作为 QWidget 的布局 现在我在每个 Qlabel 与下一个 Qlabel 之间有一个间距 我想将其删除 我尝试将标签的边距设置为 0 将 GridLayout
  • 完全彻底卸载QT Creator

    问题 如何从 Linux 机器上卸载 QT Creator 我的 Debian Jessie 机器上的安装已损坏 我尝试过重新安装 修复等 但没有成功 建议我完全卸载 获取最新版本并重新安装 问题是我不确定如何执行此操作 每次我尝试时 QT
  • 如何在Qt 5中的paintEvent上使用mouseMoveEvent?

    我是 Qt 和 c 的新手 所以我遇到了一些困难 我正在尝试创建一个小部件 它可以获取 mouseMoveEvent 位置并在鼠标位置的像素图上绘制椭圆 下面你可以看到代码 include myimage h include
  • 如何声明一个带有成员函数指针的函数

    我有一个类 其中的成员变量指向库对象 class myClassA private libraryClass libraryObject 该库类发出事件 以字符串为特征 并提供一种机制 允许客户端类指定在发出事件时应调用的成员函数 因此在m
  • QML 中可重用的字体属性[重复]

    这个问题在这里已经有答案了 在 QML 中 我希望能够定义一组字体属性以进行简单的语义重用 例如 代替 Text text This is a header font family Encode Sans weight Font Black
  • 如何将自定义 Qt 类型与 QML 信号一起使用?

    我在 Qt 5 2 qml 应用程序中创建了一个自定义类型 class Setting public QObject Q OBJECT Q PROPERTY QString key READ key WRITE setKey Q PROPE
  • 如何在Android中使用QML - QWebView

    我想在 Android 中部署一个 YouTube 应用程序 但它只能在我的电脑上运行 在安卓上不起作用 它不加载任何视频 问题仅出在 QWebView 上 我使用了与此类似的代码 http doc qt io archives qt 5
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数
  • Qt(在 Windows 上)将权限级别设置为“requireAdministrator”

    我正在使用 Qt Creator 并努力制作 exe文件默认以管理员身份运行 在线阅读所有解决方案我试图将这一行放入我的 pro file QMAKE LFLAGS MANIFESTUAC level requireAdministrato
  • Q风格所有权

    在 Qt 应用程序中使用样式时 我遇到了一个有趣的问题QStyle所有权 QStyle继承自QObject 通常接受QObject parent作为构造函数参数来管理其子级的生命周期 但QStyle的构造函数没有此构造函数参数 第一个问题
  • 在 Qt 中自动调整标签文本大小 - 奇怪的行为

    在 Qt 中 我有一个复合小部件 它由排列在 QBoxLayouts 内的多个 QLabels 组成 当小部件调整大小时 我希望标签文本缩放以填充标签区域 并且我已经在 resizeEvent 中实现了文本大小的调整 这可行 但似乎发生了某
  • QAbstractItemModel 如何表示树?

    我仍然很难理解 QAbstractItemModel 对项目的表示 有两种返回 QModelIndex 项的方法对我来说没有任何意义 QModelIndex QAbstractItemModel index int row int colu
  • 如何将 QFile 与 std::iostream 一起使用?

    是否可以像 std iostream 一样使用 QFile 我很确定那里一定有一个包装纸 问题是在哪里 我有另一个库 它需要 std istream 作为输入参数 但在我的程序中 此时我只有一个 QFile 我使用以下代码提出了自己的解决方
  • 使用 QNetworkAccessManager 的 Qt 控制台应用程序

    我正在尝试写一个Qt调用网络服务的应用程序 这是一个控制台应用程序 url 将作为命令行参数传入 我搜索了例如http程序在Qt并找到这个链接 http qt project org doc qt 5 qnetworkaccessmanag
  • QThread - 使用槽 quit() 退出线程

    我想在线程完成运行时通知对象 但是 我无法让线程正确退出 我有以下代码 处理器 cpp thread new QThread tw new ThreadWorker connect tw SIGNAL updateStatus QStrin
  • Qt - ubuntu中的串口名称

    我在 Ubuntu 上查找串行端口名称时遇到问题 如您所知 为了在 Windows 上读取串口 我们可以使用以下代码 serial gt setPortName com3 但是当我在 Ubuntu 上编译这段代码时 我无法使用这段代码 se

随机推荐

  • 2020-10-10

    闭包和装饰器 1 高阶函数 接收函数作为参数是高阶函数 将函数作为返回值返回的函数就是高阶函数 2 匿名函数 lambda函数 无名函数 语法 lambda 参数列表 表达式 filter 函数 过滤列表 第一个参数 函数 第二个参数 序列
  • Java设计模式(十四)—— 模板方法模式

    模板方法模式是指定义一个操作中算法的骨架 而将一些步骤延迟到子类中 模板方法使子类可以不改变一个算法的结构 即可重定义该算法的某些特定步骤 适合模板方法模式的情景如下 编制一个通用算法 将某些步骤的具体实现留给子类来实现 需要重构代码 将各
  • 对Linux svn保存的明文密码加密

    需求来源 随着GitHub GitLab的兴起 svn已经渐渐的没落了 从公司当初的源代码管理服务器 逐渐演变成公司的ftp服务器 最近需要部署gitlab的CI单元测试模块 而软件版本都在svn上有备份 我就希望从代码的提交 gt 到版本
  • Python统计文本数字,字母,单词量

    统计一百万位圆周率中数字0 9各自的数量 统计一本书中字母a z各自的数量 统计一本书共有多少个单词 含重复的单词 和单词量 不含重复的单词 import string class CountNums 求txt文本中数字或字母的数量 def
  • Jvm类加载机制详解---类加载器及双亲委托模型

    前面介绍了类加载的几个过程 实际中这些过程大部分都是由虚拟机本身去执行的 我们没有办法去改变或影响这些过程的执行 但是虚拟机团队将类加载阶段第一步中的 通过一个类的全限定名来获取描述该类的二进制字节流 这个动作放到虚拟机外部去实现 以便让应
  • 使用R语言绘制ovarian数据集中病例年龄分布的直方图

    使用R语言绘制ovarian数据集中病例年龄分布的直方图 直方图是一种常用的数据可视化工具 用于展示连续变量的分布情况 在R语言中 我们可以使用hist 函数轻松地创建直方图 本文将展示如何使用R语言绘制ovarian数据集中病例年龄的分布
  • String、StringBuffer和StringBuilder三者之间的区别

    最基本的区别就是String是一个字符串常量 长度不可改变 StringBuffer和StringBuilder是字符串变量 他们两个的长度可以改变 但StringBuffer是线程安全的 而StringBuilder是非线程安全的 Str
  • ** LeetCode 刷题 459

    这是一道我没做出来的简单题 5555 学习 方法1 移动匹配 如果一个字符串可以由一个字串重复获得 那么将两个相同字符串并起来 一定可以在中间再找到该字符串 class Solution public bool repeatedSubstr
  • JavaEE-过滤器和监听器 案例分析

    目录 过滤器和监听器 什么是过滤器 过滤器编程接口 接口Filter的主要方法 设计过滤器 实例1 编写一个过滤器审计用户对资源的访问 什么是监听器 监听器编程接口 设计监听器 实例 编写一个HttpSession事件监听器用来记录当前在线
  • Python pandas读取Excel 数据写入到数据库

    需求得到天眼查的法人信息数据导入到数据库中 经过多次不断试错最后使用Python导入 先上代码 import pandas as pd import sqlalchemy as sqla import os 读取Excel数据 def ge
  • net::ERR_HTTP2_PROTOCOL_ERROR 200错误

    场景 web端的环境上某一个请求报这个问题了 其他的请求正常 打开f12控制台看到异常net ERR HTTP2 PROTOCOL ERROR 200错误 排查 查看nginx的error log 报是某一个临时文件的权限不足 原因是启动n
  • CSVDE导出AD域下指定子OU的Group中所有成员的信息

    CSVDE导出AD域下指定子OU的Group中所有成员的信息 最近搞CSVDE搞的头疼 网上各种乱七八糟的资料 都是东搞一下西搞一下 好不容易找到一篇系统性的介绍使用CSVDE来对AD进行导入导出操作的文章 具体链接 http www co
  • mybatis(六) 处理枚举类型

    处理枚举类型 若想映射枚举类型 Enum 则需要从 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中选一个来使用 比如说我们想存储取近似值时用到的舍入模式 默认情况下 MyBatis 会利用 EnumT
  • python数据清洗的三个常用的处理方式!

    关于python数据处理过程中三个主要的数据清洗说明 分别是缺失值 空格 重复值的数据清洗 这里还是使用pandas来获取excel或者csv的数据源来进行数据处理 若是没有pandas的非标准库需要使用pip的方式安装一下 pip ins
  • 华为OD机试真题-分班-2023年OD统一考试(B卷)

    题目描述 幼儿园两个班的小朋友在排队时混在了一起 每位小朋友都知道自己是否与前面一位小朋友是否同班 请你帮忙把同班的小朋友找出来 小朋友的编号为整数 与前一位小朋友同班用Y表示 不同班用N表示 输入描述 输入为空格分开的小朋友编号和是否同班
  • 深度网络架构的设计技巧<一>:Can CNNs Be More Robust Than Transformers?

    导读 启发于Transformer结构 作者将三种设计引入到CNN中 加强后的CNN取得比ViT更强的鲁棒性 这三种设计 实现简单 仅仅几行代码就能实现高效的CNN结构设计 ArXiv https arxiv org abs 2206 03
  • 数据挖掘的基础

    目录 数据挖掘 一 数据挖掘理解 二 数据准备 1 缺失值处理 2 异常值处理 3 数据偏差的处理 4 数据的标准化 5 特征选择 三 数据建模 1 分类问题 2 聚类问题 3 回归问题 4 关联问题 四 评估模型 1 混淆矩阵与准确率指标
  • C语言删除一个字符串中的多余空格字符

    删除一个字符串中的多余空格字符 使得字符串中的每个单词之间只有一个空格字符 code C C char my delblank char str char newStr char start str while start start st
  • 2021年计算机保研经验帖(真平民)(上交网安、复旦、南大、同济、北航、东南)

    个人背景 中九5 数学辅修 数模省一 组织经历较丰富 干啥啥不行 活动第一名 软著及项目若干 无论文无科研 什么叫学混啊 有强烈的上海和南京倾向 有倾向是好事 但是也不要过于自信 因为各个学校的审核很迷 还是要适当海投保证自己有学上 夏令营
  • (二):Qt信号槽连接及触发原理

    经过 Qt信号槽之 准备阶段 学习 我们知道信号是函数 里面调用了 QMetaObject activate 函数 而对于接收端的槽函数或信号 是通过私有静态函数 qt static metacall 调用元方法 源头是有的 接收端也是有的