当前位置: 首页 > wzjs >正文

深圳网络营销的推广性价比高seo排名

深圳网络营销的推广,性价比高seo排名,别人 网站 粘贴 html 推广,做软件与做网站建设有什么区别博客主页:【夜泉_ly】 本文专栏:【暂无】 欢迎点赞👍收藏⭐关注❤️ 目录 前言引入connect调用链模板类型的connectQObject::connectImplQObjectPrivate::connectImpl qobject_p_p.hconnect作用总结ai对信号与槽的模拟实现 前言 面向对象&am…

博客主页:【夜泉_ly】
本文专栏:【暂无】
欢迎点赞👍收藏⭐关注❤️

在这里插入图片描述

目录

  • 前言
  • 引入
  • connect调用链
    • 模板类型的connect
    • QObject::connectImpl
    • QObjectPrivate::connectImpl
  • qobject_p_p.h
  • connect作用总结
  • ai对信号与槽的模拟实现

前言

面向对象,
这个词从开始学 C++ 我们就知道了,
但我们或许仍然不能真正理解它。
而本篇的信号与槽,
或许多多少少能加深我们对面向对象的认识。

引入

信号与槽,
本质解决的是对象之间的通信问题。
很简单的一个例子:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* btn = new QPushButton(this);btn->setText("关闭窗口");connect(btn, &QPushButton::clicked, this, &QWidget::close); // 针对不同对象。点击按钮,关闭窗口
}

在这里,
connect 将一个按钮和一个控件建立了连接。
点击按钮,
按钮会告诉控件:
你该关闭了。

很明显按钮和控件是两个不同的对象,
而它们能够通信,
借助的就是信号与槽。

connect调用链

模板类型的connect

为了加深理解,
下面我们来简单看看Qt中对刚刚的 connect 的处理。
首先,当我们写下:

connect(btn, &QPushButton::clicked, this, &QWidget::close);

会调用 qobject.h 的模板类型的 connect (227行左右)

//connect with context
template <typename Func1, typename Func2>
static inline QMetaObject::Connectionconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType *context, Func2 &&slot,Qt::ConnectionType type = Qt::AutoConnection)

之后会进行一系列的检查,
用了大量 TMP 的知识,
我暂时看不懂。
总之,大概检查了信号和槽的各种类型后,
调用了 connectImpl
implimplementation(实现)的缩写,
所以这里才是连接信号和槽的地方。
(那 connect 的几十行代码全用来检查了?恐怖如斯)
connect 函数末尾:在这里插入图片描述

QObject::connectImpl

这个 connectImpl 也有很多地方实现了,
不过根据参数类型,
我觉得它调的是这个 QObject::connectImpl
qobject.cpp 5324行左右

QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,const QObject *receiver, void **slot,QtPrivate::QSlotObjectBase *slotObjRaw, Qt::ConnectionType type,const int *types, const QMetaObject *senderMetaObject)

查了查,
Qt 的信号是由 moc 工具生成的元数据,
依赖于 QObjectQMetaObject
而这个 QObject::connectImpl 的作用就是:
使用 QMetaObject 的元信息查找信号的索引 signal_index
然后在函数末尾调用了 QObjectPrivate::connectImpl

return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, types, senderMetaObject);

QObjectPrivate::connectImpl

qobject.cpp 5370行左右

QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,const QObject *receiver, void **slot,QtPrivate::QSlotObjectBase *slotObjRaw, int type,const int *types, const QMetaObject *senderMetaObject)

这下才是真的来到核心实现了🤣。
我觉得最重要的两句话:

std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
QObjectPrivate::get(s)->addConnection(signal_index, c.get());

这里创建了一个 QObjectPrivate::Connection 对象,
在里面保存信号和槽的连接信息。

又用 QObjectPrivate::get(s)
即指向信号发送者( sender )的 QObjectPrivate 指针,
调用 addConnection()
而 addConnection。。。

qobject_p_p.h

我们还是先看看 QObjectPrivate 中的几个结构体吧:

struct Connection;
struct ConnectionData;
struct ConnectionList;
struct ConnectionOrSignalVector;
struct SignalVector;
struct Sender;
struct TaggedSignalVector;

定义在 qobject_p_p.h 中,
共同构成了 Qt 信号与槽机制的底层实现。

Connection的关键字段(我认为的):

struct QObjectPrivate::Connection : public ConnectionOrSignalVector
{QObject *sender;QAtomicPointer<QObject> receiver;union {StaticMetaCallFunction callFunction;QtPrivate::QSlotObjectBase *slotObj;};signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking
};

Connection 表示信号与槽之间的一个具体连接,
是个双链表的节点(指针被我省略了,因为我看不懂)。

  • sender 发出信号的对象
  • receiver 接收信号的对象
  • union 中,提供两种方式去调用函数
    且因为是 union,所以同时只存在一个方法
    Qt会根据你传的槽去选择:
    • callFunction 对应 成员函数
    • slotObj 对应 lambda 表达式、仿函数等复杂点的对象

ConnectionData 的关键字段(我认为的):

struct QObjectPrivate::ConnectionData
{QAtomicPointer<SignalVector> signalVector;Connection *senders = nullptr;Sender *currentSender = nullptr;
};
  • signalVector 存的与对象相关的所有信号的连接信息。
    每个元素对应一个 ConnecionList
    存储了与该信号相关的所有槽的连接信息。
    在这里插入图片描述

  • senders 存的连接到当前对象槽的信号的连接信息。
    从类型 Connection 看出,这是个双链表。
    在这里插入图片描述

  • currentSender 指的是当前被激活的信号发送者,
    当有信号被激活,会有个 Sender 对象被创建,并连接到这里。
    具体的细节。。嘶,又要跳文件吗 !?
    好像激活和 qobjectdefs.hQMetaObject::activate 有关,
    暂时不看了。
    Sender 的构造可以看看,
    这里体现了连接到 currentSender 的过程:

    Sender(QObject *receiver, QObject *sender, int signal, ConnectionData *receiverConnections): receiver(receiver), sender(sender), signal(signal)
    {if (receiverConnections) {previous = receiverConnections->currentSender;receiverConnections->currentSender = this;}
    }
    

connect作用总结

那么看到这里,
似乎 addConnection 不太需要看了,
Qt的 SignalVector 使用了非常规的方法表示数组,
主要利用的是指针的偏移,
所以相关的代码都涉及大量的指针操作,
提高了性能,
但降低了我这种fw的阅读体验。

总结一下 QObjectPrivate::connectImpl 的主要作用吧:
创建连接信息,用的 Connection 结构体。
将连接信息添加到发送者的 signalVector
将连接信息添加到接收者的 senders

最后回到开头的例子:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* btn = new QPushButton(this);btn->setText("关闭窗口");connect(btn, &QPushButton::clicked, this, &QWidget::close); // 针对不同对象。点击按钮,关闭窗口
}

发送者就是 btn ,是个按钮。
接收者就是 this , 是个控件。
通过 connect
btnsingalVectorclicked 信号的 ConnecionList 多了一个 Connection
thissenders 双链表中 也多了一个 Connection
而这两个地方指向的是同一个 Connection 结构体
而这个 Connection 的内容:
sender 就是 btn,即按钮
receiver 就是 this,即控件
callFunction 就是 QWidget::close。(因为 QWidget::close 是个成员函数,所以union 中是 callFunction,而不是 slotObj)
signal_index 就是 QPushButton::clicked 信号对应的下标。

这就是建立连接的过程了。
至于调用。。力竭了,不想看了。。

ai对信号与槽的模拟实现

最后,看看ai的模拟实现吧:
类型检查虽然非必须,
但 TMP 真的帅啊。。。

#include <iostream>
#include <vector>
#include <functional>
#include <tuple>// 检查每一对参数类型是否兼容的类型特征
template <typename SignalArgsTuple, typename SlotArgsTuple>
struct CheckCompatibleArguments;// 基本情况:参数列表为空
template <>
struct CheckCompatibleArguments<std::tuple<>, std::tuple<>> {static constexpr bool value = true;
};// 递归情况:逐一检查所有参数对
template <typename SignalArg, typename... SignalRest,typename SlotArg, typename... SlotRest>
struct CheckCompatibleArguments<std::tuple<SignalArg, SignalRest...>, std::tuple<SlotArg, SlotRest...>
> {static constexpr bool value =std::is_convertible<SignalArg, SlotArg>::value&&CheckCompatibleArguments<std::tuple<SignalRest...>, std::tuple<SlotRest...>>::value;
};// Signal类定义,用于表示信号,管理连接的槽
template <typename... Args>
class Signal {std::vector<std::function<void(Args...)>> slots;public:// 连接槽函数到当前信号template <typename F>void connect(F&& f) {slots.emplace_back(std::forward<F>(f));}// 触发信号,传播参数至所有已连接的槽void emit(Args... args) {for (auto& slot : slots) {slot(args...);}}
};// 全局connect函数,用于连接信号和成员函数槽
template <typename Receiver, typename... SignalArgs, typename... SlotArgs>
void connect(Signal<SignalArgs...>& signal,Receiver* receiver,void (Receiver::* slot)(SlotArgs...)
) {// 确保信号和槽参数个数匹配static_assert(sizeof...(SignalArgs) == sizeof...(SlotArgs),"Signal and slot have different number of arguments");// 确保参数类型兼容static_assert(CheckCompatibleArguments<std::tuple<SignalArgs...>,std::tuple<SlotArgs...>>::value,"Signal and slot arguments are not compatible");signal.connect([receiver, slot](SignalArgs... args) {(receiver->*slot)(args...);});
}// 示例发送者类:按钮
class Button {
public:Signal<int> clicked;  // 带整数参数(点击次数)的信号void press() {static int count = 0;clicked.emit(++count); // 发出信号}
};// 示例接收者类:标签
class Label {
public:void showCount(int num) {std::cout << "点击次数:" << num << std::endl;}
};int main() {// 测试 1: 基本测试:按钮点击信号和标签的显示槽连接Button button;Label label;// 连接按钮的点击信号到标签的显示槽connect(button.clicked, &label, &Label::showCount);// 模拟用户点击按钮button.press(); // 输出:点击次数:1button.press(); // 输出:点击次数:2std::cout << "-------------------" << std::endl;// 测试 2: 多个槽连接到同一个信号Button button2;Label label2;Label label3;// 连接按钮的点击信号到多个槽connect(button2.clicked, &label2, &Label::showCount);connect(button2.clicked, &label3, &Label::showCount);// 模拟用户点击按钮button2.press(); // 输出:点击次数:1button2.press(); // 输出:点击次数:2std::cout << "-------------------" << std::endl;// 测试 3: 使用不同类型的信号和槽class StringLabel {public:void showString(const std::string& str) {std::cout << "显示字符串:" << str << std::endl;}};Signal<std::string> stringSignal;StringLabel stringLabel;// 连接信号和槽connect(stringSignal, &stringLabel, &StringLabel::showString);// 发射一个字符串信号stringSignal.emit("Hello, world!"); // 输出:显示字符串:Hello, world!std::cout << "-------------------" << std::endl;// 测试 4: 测试无参信号Signal<> noArgSignal;class NoArgLabel {public:void notify() {std::cout << "信号发射了!" << std::endl;}};NoArgLabel noArgLabel;// 连接无参信号和槽connect(noArgSignal, &noArgLabel, &NoArgLabel::notify);// 发射无参信号noArgSignal.emit(); // 输出:信号发射了!std::cout << "-------------------" << std::endl;// 测试 5: 参数类型不兼容的错误(编译时错误)// Uncommenting the code below will result in a compilation error.// Signal<double> doubleSignal;// connect(doubleSignal, &label, &Label::showCount); // 编译错误:类型不兼容return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

http://www.dtcms.com/wzjs/599581.html

相关文章:

  • 哈尔滨小程序开发seo学院培训班
  • 如何建设个人免费网站教程视频怎么做网站服务器吗
  • .net网站开发工程师wordpress本地建站教程
  • 建公司网站建设明细报价表兰州网络推广昔年下拉博客
  • seo网站推广简历怎么样做电商赚钱
  • 用什么网站做浏览器主页wordpress会员登录查询
  • 公司网站维护分工怎么查看一个网站的浏览量
  • 杭州科技公司网站建设湛江做网站多少钱
  • 饮食网站模板沈阳高端做网站建设
  • 九江 网站建设网站seo如何做
  • 潍坊哪个网站建设公司好线上赚钱正规平台
  • 深圳网站建设注册深圳龙华鸿宇大厦网站建设
  • 分类信息网站怎么建设网站模板免费下载php
  • 李洋网络做网站怎么样文山州建设局网站
  • 阿里巴巴电子商务网站建设目的做门户网站的市场价格
  • 怎样维护网站的安全和备份企业推广策划书模板
  • 建设网站需要花费多少钱精东影视文化传媒有限公司官网
  • 网站标题更换抓取资源的网站怎么做
  • 怎么建设淘客自己的网站_企业应用系统有哪些
  • 海南智能网站建设设计正规的饰品行业网站开发
  • 东莞网站高端建设儿童编程教学入门教程
  • 无锡找做网站中国建设教育协会网站培训中心
  • 毕业设计h5网站制作百度指数支持数据下载吗
  • 免费站推广网站在线上海建个人网站比较好的公司
  • 福田网站开发网站友情链接形式
  • 个人网站 wordpressapp开发公司历程概述
  • 旅游网站域名应该如何设计熊掌号接入wordpress
  • wed网站开发是什么苏州微网站建设公司哪家好
  • 光环时讯网站北海做网站网站建设哪家好
  • 做汤的网站网页链接提取工具