Qt源码解析之QObject

省去大部分virtual和public方法后,Qobject主要剩下以下成员:

//qobject.h
class Q_CORE_EXPORT Qobject{Q_OBJECTQ_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)Q_DECLARE_PRIVATE(QObject)
public:Q_INVOKABLE explicit QObject(QObject *parent=nullptr);virtual ~QObject();//...
protected:QObject(QObjectPrivate &dd, QObject *parent = nullptr);//...
protected:QScopedPointer<QObjectData> d_ptr;static const QMetaObject staticQtMetaObject;//...
private:Q_DISABLE_COPY(QObject)//...
}

一、Q_OBJECT

#define Q_OBJECT \
public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \QT_TR_FUNCTIONS \
private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \QT_WARNING_POP \struct QPrivateSignal {}; \QT_ANNOTATE_CLASS(qt_qobject, "")1、宏 QT_WARNING_PUSH 和 QT_WARNING_POP用于保存和恢复编译器的警告状态,以便在宏定义内部做一些修改或设置,而不影响用户定义的警告状态。
2、Q_OBJECT_NO_OVERRIDE_WARNING和Q_OBJECT_NO_ATTRIBUTES_WARNING这两个宏用于控制是否发出关于未覆盖(override)的警告或者关于某些属性的警告。
3、QT_TR_FUNCTIONS这个宏用于启用Qt的国际化(internationalization)功能,使得文本可以被翻译为不同的语言。
4、Q_DECL_HIDDEN_STATIC_METACALL在qobjectdefs.h有定义:
# define Q_DECL_HIDDEN_STATIC_METACALL Q_DECL_HIDDEN
使用 Q_DECL_HIDDEN 可以将类或函数标记为在外部接口中隐藏的,从而使它们对库的用户不可见。这对于避免一些链接时的符号冲突和提高库的封装性很有帮助。这个宏可能会被翻译成 __attribute__((visibility("hidden")))。也就是说qt_static_metacall这个函数没用到,我们忽略。

去除和编译器相关的宏,Q_OBJECT剩下的关键部分:

//qobjectdefs.h
#define Q_OBJECT \
public: \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); Q_OBJECT宏声明了1个QMetaObject变量和3个QMetaObject相关的虚函数。QMetaObject类非常重要,和元对象系统相关。

二、Q_PROPERTY

//qobjectdefs.h
#define Q_PROPERTY(...) QT_ANNOTATE_CLASS(qt_property, __VA_ARGS__)
#define QT_ANNOTATE_CLASS(type, ...)在 qobjectdefs.h 中我们并没有看到 Q_PROPERTY 的准确定义。很多Qt的宏和特殊功能是通过moc生成的代码而不是在头文件中显式定义的。C++编译器能够识别 Q_PROPERTY 宏,是因为moc编译时生成了相应的代码。
使用Q_PROPERTY后,相当于把属性纳入了元对象系统,而且给出了一段Q_PROPERTY更细致的声明:
Q_PROPERTY(type name(READ getFunction [WRITE setFunction] |MEMBER memberName [(READ getFunction | WRITE setFunction)])[RESET resetFunction][NOTIFY notifySignal][REVISION int | REVISION(int[, int])][DESIGNABLE bool][SCRIPTABLE bool][STORED bool][USER bool][BINDABLE bindableProperty][CONSTANT][FINAL][REQUIRED])

三、Q_DECLARE_PRIVATE

//qglobal.h
#define Q_DECLARE_PRIVATE(Class) \inline Class##Private* d_func() \{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } \inline const Class##Private* d_func() const \{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } \friend class Class##Private;加入参数并翻译过后:
inline QObjectPrivate* d_func()
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<QObjectPrivate *>(qGetPtrHelper(d_ptr));) }
inline const QObjectPrivate* d_func() const
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const QObjectPrivate *>(qGetPtrHelper(d_ptr));) }
friend class QObjectPrivate;qGetPtrHelper()方法的定义:
//qglobal.h
template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }qGetPtrHelper是一个模板函数,其目的是为了获取指针或类似指针的数据。
Q_CAST_IGNORE_ALIGN用于禁用GCC编译器的 -Wcast-align 警告。Q_DECLARE_PRIVATE宏定义了2个函数和1个友元类。2个d_func只是签名不同,传入参数d_ptr,都返回一个QObjectPrivate*类型的指针,而且友元类的名称也是QObjectPrivate。

四、QObjectData和QObjectPrivate

关于变量QScopedPointer<QObjectData> d_ptr:QScopedPointer类是用于存储指向动态分配对象的指针,并在其销毁时删除它,确保指向的对象在当前作用域消失时将被删除。
所以QScopedPointer<QObjectData>是一个QObjectData的指针。QObjectData定义:
//qobject.h
class Q_CORE_EXPORT QObjectData {//防止对象拷贝Q_DISABLE_COPY(QObjectData)
public:QObjectData() = default;virtual ~QObjectData() = 0;QObject *q_ptr;QObject *parent;QObjectList children;uint isWidget : 1;uint blockSig : 1;uint wasDeleted : 1;uint isDeletingChildren : 1;uint sendChildEvents : 1;uint receiveChildEvents : 1;uint isWindow : 1; //for QWindowuint deleteLaterCalled : 1;uint unused : 24;int postedEvents;QDynamicMetaObjectData *metaObject;QMetaObject *dynamicMetaObject() const;
#ifdef QT_DEBUGenum { CheckForParentChildLoopsWarnDepth = 4096 };
#endif
};上面说到d_func函数传入参数d_ptr,返回的QObjectPrivate*类型的指针,而d_ptr是QObjectData,那也就是说QObjectPrivate是QObjectData的子类。我们且看QObjectPrivate的定义:
//qobject_p.h
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{Q_DECLARE_PUBLIC(QObject)
public:struct ExtraData{//...};//和信号&槽相关struct ConnectionOrSignalVector{//...};//和信号&槽相关struct Connection : public ConnectionOrSignalVector{//...};//和信号&槽相关struct Sender{//...};//和信号&槽相关struct ConnectionData{//...};QObjectPrivate(int version = QObjectPrivateVersion);virtual ~QObjectPrivate();
public:ExtraData *extraData;QAtomicPointer<QThreadData> threadData;using ConnectionDataPointer = QExplicitlySharedDataPointer<ConnectionData>;QAtomicPointer<ConnectionData> connections;union {QObject *currentChildBeingDeleted;QAbstractDeclarativeData *declarativeData;};QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;  
}Q_DECLARE_PUBLIC(QObject)定义:
//qglobal.h
#define Q_DECLARE_PUBLIC(Class)                                    \inline Class* q_func() { return static_cast<Class *>(q_ptr); } \inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \friend class Class;翻译过后:
inline QObject* q_func() { return static_cast<QObject *>(q_ptr); } 
inline const QObject* q_func() const { return static_cast<const QObject *>(q_ptr); } \
friend class QObject;
这个宏实际上定义了2个签名不一样的函数q_func(),返回q_ptr指针,声明了QObject是友元类。QObjectPrivate的构造器定义如下:
//qobject.cpp
QObjectPrivate::QObjectPrivate(int version): threadData(nullptr), currentChildBeingDeleted(nullptr)
{checkForIncompatibleLibraryVersion(version);// QObjectData initializationq_ptr = nullptr;parent = nullptr;                           // no parent yet. It is set by setParent()isWidget = false;                           // assume not a widget objectblockSig = false;                           // not blocking signalswasDeleted = false;                         // double-delete catcherisDeletingChildren = false;                 // set by deleteChildren()sendChildEvents = true;                     // if we should send ChildAdded and ChildRemoved events to parentreceiveChildEvents = true;postedEvents = 0;extraData = nullptr;metaObject = nullptr;isWindow = false;deleteLaterCalled = false;
}
基本上是对继承下来的变量和自身变量进行初始化。

五、QObject()

当实例化一个继承自QObject的对象时,首先会调用QObject的构造器,构造器开始构造对象模型的世界,我们且看QObject构造函数QObject()的定义:
//qobject.cpp
QObject::QObject(QObject *parent): QObject(*new QObjectPrivate, parent)
{
}//qobject.cpp
QObject::QObject(QObjectPrivate &dd, QObject *parent): d_ptr(&dd)
{Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");Q_D(QObject);d_ptr->q_ptr = this;auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : QThreadData::current();threadData->ref();d->threadData.storeRelaxed(
threadData);if (parent) {QT_TRY {if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr, 
threadData))parent = nullptr;if (d->isWidget) {if (parent) {d->parent = parent;d->parent->d_func()->children.append(
this);}// no events sent here, this is done at the end of the QWidget constructor} else {setParent(parent);}} QT_CATCH(...) {threadData->deref();QT_RETHROW;}}
#if QT_VERSION < 0x60000qt_addObject(this);
#endifif (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);Q_TRACE(QObject_ctor, this);
}public的构造函数实际上是调用了protected的构造函数。
默认新建了一个QObjectPrivate并作为构造函数参数传入,赋值给了d_ptr。变量QScopedPointer<QObjectData> d_ptr在构造函数里实际被赋值为其新建的子实例QObjectPrivate。Q_D(QObject)定义:
//qglobal.h
#define Q_D(Class) Class##Private * const d = d_func()
调用d_func()得到QObjectPrivate* 并赋值给d,此时d和d_ptr都指向前面实例化的QObjectPrivate。d_ptr->q_ptr = this;
将QObjectPrivate->q_ptr设置为自身。//qobject.cpp
auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : QThreadData::current();
threadData->ref();
d->threadData.storeRelaxed(
threadData);
检查 parent 是否非空且它所属的线程是否为空,如果都不空的话,获取parent的线程数据;否则获取当前的线程数据。将线程数据存储到对象内部的数据结构中。//qobject.cpp
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr, threadData))parent = nullptr;
检查parent和当前对象是否在相同的线程中,如果不在相同线程中,将 parent 设置为 nullptr。//qobject.cpp
if (d->isWidget) {if (parent) {d->parent = parent;d->parent->d_func()->children.append(this);}
}else{//...
}
如果对象是一个QWidget,parent不空,则建立起对象和parent的联系,对象的父对象就是parent,parent的children添加该对象。//qobject.cpp
if (d->isWidget) {//...
} else {setParent(parent);
}
如果对象不是QWidget,通过setParent(parent)设置父对象。setParent()的定义:
//qobject.cpp
void QObject::setParent(QObject *parent)
{Q_D(QObject);Q_ASSERT(!d->isWidget);d->setParent_helper(parent);
}
继续调用d->setParent_helper(parent)。setParent_helper()的定义:
void QObjectPrivate::setParent_helper(QObject *o)
{Q_Q(QObject);Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself");
#ifdef QT_DEBUGconst auto checkForParentChildLoops = qScopeGuard(
[&](){int depth = 0;auto p = parent;while (p) {if (++depth == CheckForParentChildLoopsWarnDepth) {qWarning(
"QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; ""this is undefined behavior",q, q->metaObject()->className(), qPrintable(q->objectName()));}p = p->parent();}});
#endifif (o == parent)return;if (parent) {QObjectPrivate *parentD = parent->d_func();if (parentD->isDeletingChildren && wasDeleted&& parentD->currentChildBeingDeleted == q) {// don't do anything since QObjectPrivate::deleteChildren() already// cleared our entry in parentD->children.} else {const int index = parentD->children.indexOf(q);if (index < 0) {// we're probably recursing into setParent() from a ChildRemoved event, don't do anything} else if (parentD->isDeletingChildren) {parentD->children[index] = 0;} else {parentD->children.removeAt(index);if (sendChildEvents && parentD->receiveChildEvents) {QChildEvent e(QEvent::ChildRemoved, q);QCoreApplication::sendEvent(parent, &e);}}}}parent = o;if (parent) {// object hierarchies are constrained to a single threadif (threadData != parent->d_func()->threadData) {qWarning(
"QObject::setParent: Cannot set parent, new parent is in a different thread");parent = nullptr;return;}parent->d_func()->children.append(q);if(sendChildEvents && parent->d_func()->receiveChildEvents) {if (!isWidget) {QChildEvent e(QEvent::ChildAdded, q);QCoreApplication::sendEvent(parent, &e);}}}if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}Q_Q(QObject)的定义:
//qglobal.h
#define Q_Q(Class) Class * const q = q_func()
通过q_func()获取QObjectPrivate的q_ptr,在上面我们知道q_ptr指向了QObject,所以q和q_ptr都指向QObject。#ifdef QT_DEBUGconst auto checkForParentChildLoops = qScopeGuard(
[&](){int depth = 0;auto p = parent;while (p) {if (++depth == CheckForParentChildLoopsWarnDepth) {qWarning(
"QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; ""this is undefined behavior",q, q->metaObject()->className(), qPrintable(q->objectName()));}p = p->parent();}});
#endif
这一段通过warning可以推断出是在检测父子关系链中是否存在循环,如果循环链深度超过阈值,则警告。if (o == parent)return;
如果已经设置过parent且没变,直接返回。//如果已经有parent
if (parent) {//获取父对象的QObjectPrivateQObjectPrivate *parentD = parent->d_func();//检查父对象是否正在删除其子对象,当前对象是否已经被删除,前对象是否是父对象正在删除的子对象。//如果这些条件都成立,就跳过后续的处理,因为在删除子对象的过程中已经做了清理工作。if (parentD->isDeletingChildren && wasDeleted&& parentD->currentChildBeingDeleted == q) {// don't do anything since QObjectPrivate::deleteChildren() already// cleared our entry in parentD->children.} else {//获取当前对象在其父对象的子对象列表中的索引const int index = parentD->children.indexOf(q);//如果索引为负数,可能表示正在从 ChildRemoved 事件中递归到 setParent(),这时不执行任何操作。if (index < 0) {// we're probably recursing into setParent() from a ChildRemoved event, don't do anything} else if (parentD->isDeletingChildren) {//如果父对象正在删除其子对象,将相应的子对象指针更新为0。parentD->children[index] = 0;} else {//否则,从父对象的子对象列表中移除当前对象parentD->children.removeAt(index);//发送一个 ChildRemoved 事件给父对象。if (sendChildEvents && parentD->receiveChildEvents) {QChildEvent e(QEvent::ChildRemoved, q);QCoreApplication::sendEvent(parent, &e);}}}
}
上面这一段是在已有perent的情况下,断开parent和当前对象的联系,并确保在移除子对象时做了适当的清理和事件通知。实际上是为下面刷新parent做准备。parent = o;//更新parent//parent赋值后
if (parent) {// object hierarchies are constrained to a single thread// 对象层次结构受限于单个线程// 比较当前对象的线程数据和父对象的线程数据,如果它们不一致if (threadData != parent->d_func()->threadData) {qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");//父对象置空parent = nullptr;//直接返回return;}//将当前对象添加到父对象的子对象列表中。parent->d_func()->children.append(q);if(sendChildEvents && parent->d_func()->receiveChildEvents) {if (!isWidget) {//将这个事件发送给父对象QChildEvent e(QEvent::ChildAdded, q);QCoreApplication::sendEvent(parent, &e);}}
}
上面这一段是在设置对象的父对象后进行一些检查,确保父对象线程数据和该对象的一致,否则将parent设为nullptr,随后发送相应的ChildAdded事件给parent。
setParent_helper函数主要做了两件事:
1)确保旧parent安全撤离。
2)确保新parent正确设置。简单概括一下构造函数QObject()的内容:
1)新建QObjectPrivate并赋值给d_ptr。
2)赋值d_ptr->q_ptr为对象本身。
3)初始化threadData。
4)检查当前对象和parent是否在同一线程.
5)为当前对象和parent设置关联.

六、Q_DISABLE_COPY()

//qglobal.h
#define Q_DISABLE_COPY(Class) \Class(const Class &) = delete;\Class &operator=(const Class &) = delete;这里删除了拷贝构造函数和拷贝赋值操作符,确保QObject不能被拷贝构造或赋值。

觉得有帮助的话,打赏一下呗。。

           

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

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

相关文章

STM32-OC输出比较和PWM

本内容基于江协科技STM32视频内容&#xff0c;整理而得。 文章目录 1. OC输出比较和PWM1.1 OC输出比较1.2 PWM&#xff08;脉冲宽度调制&#xff09;1.3 输出比较通道&#xff08;高级&#xff09;1.4 输出比较通道&#xff08;通用&#xff09;1.5 输出比较模式1.6 PWM基本结…

MATLAB常用语句总结7

MATLAB总结7&#xff1a;常见错误归纳 本篇专门用于记录一些应试技巧 文章目录 MATLAB总结7&#xff1a;常见错误归纳前言一、一些小定义和小技巧二、蒙塔卡罗求解方法1.函数的定义2.函数引用3.代码量较少的蒙塔卡罗 三、函数引用与多变量四、矩阵引用五、非线性函数&#xff…

14-39 剑和诗人13 - 顶级大模型测试分析和建议

​​​​​ 随着对高级语言功能的需求不断飙升&#xff0c;市场上涌现出大量语言模型&#xff0c;每种模型都拥有独特的优势和功能。然而&#xff0c;驾驭这个错综复杂的生态系统可能是一项艰巨的任务&#xff0c;开发人员和研究人员经常面临选择最适合其特定需求的模型的挑战。…

哈弗架构和冯诺伊曼架构

文章目录 1. 计算机体系结构 2. 哈弗架构&#xff08;Harvard Architecture&#xff09; 3. 改进的哈弗架构 4. 冯诺伊曼架构&#xff08;Von Neumann Architecture&#xff09; 5. 结构对比 1. 计算机体系结构 计算机体系结构是指计算机系统的组织和实现方式&#xff0c…

Python | Leetcode Python题解之第220题存在重复元素III

题目&#xff1a; 题解&#xff1a; class Solution(object):def containsNearbyAlmostDuplicate(self, nums, k, t):from sortedcontainers import SortedSetst SortedSet()left, right 0, 0res 0while right < len(nums):if right - left > k:st.remove(nums[left]…

Python基础问题汇总

为什么学习Python&#xff1f; 易学易用&#xff1a;Python语法简洁清晰&#xff0c;易于学习。广泛的应用领域&#xff1a;适用于Web开发、数据科学、人工智能、自动化脚本等多种场景。强大的库支持&#xff1a;拥有丰富的第三方库&#xff0c;如NumPy、Pandas、TensorFlow等…

Sass 语法

文章目录 编译变量 \$嵌套 {} > \~导入 import注释 // /*\* \**/混入 mixin/include继承 extend数据类型运算控制 if/for/each/while函数 function媒体查询 media根发出 at-root警告warn/错误error/调试debug 编译 编译命令 单文件转换命令 sass input.scss output.css单…

数学基础 -- 反函数

反函数技术文档 反函数的定义 反函数&#xff08;inverse function&#xff09;是指一种将函数的输出反过来作为输入&#xff0c;从而恢复原来输入的函数。具体来说&#xff0c;如果有一个函数 f f f&#xff0c;它把一个值 x x x 映射到一个值 y y y&#xff0c;即 f ( …

68.WEB渗透测试-信息收集- WAF、框架组件识别(8)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;67.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;7&#xff09; 右边这些是waf的…

Mean teacher are better role models-论文笔记

论文笔记 资料 1.代码地址 2.论文地址 https://arxiv.org/pdf/1703.01780 3.数据集地址 CIFAR-10 https://www.cs.utoronto.ca/~kriz/cifar.html 论文摘要的翻译 最近提出的Temporal Ensembling方法在几个半监督学习基准中取得了最先进的结果。它维护每个训练样本的标签…

PCIe驱动开发(1)— 开发环境搭建

PCIe驱动开发&#xff08;1&#xff09;— 开发环境搭建 一、前言 二、Ubuntu安装 参考: VMware下Ubuntu18.04虚拟机的安装 三、QEMU安装 下载网站&#xff1a; https://download.qemu.org 下载文件&#xff1a;qemu-4.1.0-rc5.tar.xz 使用如下命令解压&#xff1a; tar …

opencv 设置超时时间

经常爬视频数据&#xff0c;然后用opencv做成图片 因此设置超时时间很重要 cap.set(cv2.CAP_PROP_FPS, timeout_ms) for idx, row in data.iterrows(): if idx < 400: continue try: # 打开视频文件 timeout_ms 5000 cap cv2.VideoCapture(row[PLAY_URL]) cap.set(cv2.C…

Linux下使用libiw进行无线信号扫描的实例

打开电脑连接wifi是一件很平常的事情,但这些事情通常都是操作系统下的wifi管理程序替我们完成的,如何在程序中扫描wifi信号其实资料并不多,前面已经有两篇文章介绍了如何使用ioctl()扫描wifi信号,但其实在Linux下有一个简单的库对这些ioctl()的操作进行了封装,这个库就是l…

深入追踪:IPython 中 %tb 命令的异常追踪栈使用指南

深入追踪&#xff1a;IPython 中 %tb 命令的异常追踪栈使用指南 在 IPython 的强大功能中&#xff0c;%tb 命令是一个调试工具&#xff0c;用于在出现异常时查看详细的异常追踪栈信息。这对于开发者来说是一个不可或缺的功能&#xff0c;因为它提供了对错误发生上下文的深入了…

Unity 中,常用的 UnityEngine.Events 中的几个重要的事件处理函数

在 Unity 中&#xff0c;常用的 UnityEngine.Events 中的几个重要的事件处理函数包括&#xff1a; UnityEvent UnityEvent 是 Unity 提供的一种事件系统&#xff0c;可以用来实现脚本与场景中的对象之间的互动。它可以用来定义和响应事件&#xff0c;如按钮点击、物体碰撞等。示…

GPT-5或重塑我们的工作与生活

引言 在人工智能发展的浪潮中&#xff0c;每一次技术的革新都如同潮水般涌来&#xff0c;带来前所未有的机遇与挑战。当新一代大语言模型GPT-5即将登场的消息传来&#xff0c;我们不禁要问&#xff1a;它将如何重塑我们的工作和日常生活&#xff1f;又将开启哪些崭新的应用场景…

故障模式与影响分析(FMEA)的概念

故障模式与影响分析&#xff08;FMEA&#xff09;的概念 故障模式与影响分析&#xff08;Failure Mode and Effects Analysis&#xff0c;FMEA&#xff09;是一种系统性评估方法&#xff0c;用于识别产品设计或过程中可能发生的潜在故障模式&#xff0c;以及这些故障模式对系统…

制作爬取4399游戏名称软件

def 爬取4399(): #发送请求并且拿到源代码 import requests 链接https://www.4399.com/ #网站链接 请求头{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0} #构造请求头用于爬取网站源代码使用 网站源代码…

MySQL远程登录

root是超级管理员&#xff0c;默认情况下&#xff0c;root不能作为远程登录的用户名&#xff0c;远程登录前&#xff0c;需要将登录的数据库在本地登录&#xff0c;修改权限&#xff0c;输入&#xff1a; update user set host & where user root ; 回车键&#xff0c…

clickhouse高可用可拓展部署

clickhouse高可用&可拓展部署 1.部署架构 1.1高可用架构 1.2硬件资源 部署服务 节点名称 节点ip 核数 内存 磁盘 zookeeper zk-01 / 4c 8G 100G zk-02 / 4c 8G 100G zk-03 / 4c 8G 100G clikehouse ck-01 / 32c 128G 2T ck-02 / 32c 128G 2T ck-03 / 32c 128G 2T ck-04 /…