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不能被拷贝构造或赋值。
觉得有帮助的话,打赏一下呗。。

相关文章:
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_I…...
【算法专题】模拟算法题
模拟算法题往往不涉及复杂的数据结构或算法,而是侧重于对特定情景的代码实现,关键在于理解题目所描述的情境,并能够将其转化为代码逻辑。所以我们在处理这种类型的题目时,最好要现在演草纸上把情况理清楚,再动手编写代…...
分库分表真的适合你的系统吗?
曾几何时,“并发高就分库,数据大就分表”已经成了处理 MySQL 数据增长问题的圣经。 面试官喜欢问,博主喜欢写,候选人也喜欢背,似乎已经形成了一个闭环。 但你有没有思考过,分库分表真的适合你的系统吗&am…...
9 redis,memcached,nginx网络组件
课程目标: 1.网络模块要处理哪些事情 2.reactor是怎么处理这些事情的 3.reactor怎么封装 4.网络模块与业务逻辑的关系 5.怎么优化reactor? io函数 函数调用 都有两个作用:io检测 是否就绪 io操作 1. int clientfd = accept(listenfd, &addr, &len); 检测 全连接队列…...
【MySQL】事务四大特性以及实现原理
事务四大特性 原子性(Atomicity) 事务中的所有操作要么全部完成,要么全部不执行。如果事务中的任何一步失败,整个事务都会被回滚,以保持数据的完整性。 一致性(Consistency) 事务应确保数据库…...
【控制Android.bp的编译】
1.首先Android.bp的语法是不支持if 条件语句的 2.查到可以用enabled来控制Android.bp中的模块是否参与编译,但是并不能实现动态的控制,比如你需要根据获取到的安卓版本来控制一个Android.bp是否编译,是无法做到的。enabled只能是固定的true或…...
【车载开发系列】J-Link/JFlash 简介与驱动安装方法
【车载开发系列】J-Link/JFlash 简介与驱动安装方法 【车载开发系列】J-Link/JFlash 简介与驱动安装方法 【车载开发系列】J-Link/JFlash 简介与驱动安装方法一. 软件介绍二. 下载安装包二. 开始安装三. 确认安装四. J-Flash的使用 一. 软件介绍 J-Link是SEGGER公司为支持仿真…...
207 课程表
题目 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …...
罗剑锋的C++实战笔记学习(一):const、智能指针、lambda表达式
1、const 1)、常量 const一般的用法就是修饰变量、引用、指针,修饰之后它们就变成了常量,需要注意的是const并未区分出编译期常量和运行期常量,并且const只保证了运行时不直接被修改 一般的情况,const放在左边&…...
宁德时代天行发布,商用车超充时代来临
近日,宁德时代正式推出商用动力电池品牌——“宁德时代天行”,同时发布“宁德时代天行轻型商用车(L)-超充版”和“宁德时代天行轻型商用车(L)-长续航版”两款产品,可实现4C超充能力和500km的实况…...
硅纪元应用评测 | 弱智吧大战GPT4o和Claude 3.5 Sonnet
"硅纪元AI应用测评"栏目,深入解析和评测最新的人工智能应用,提供专业见解和实用建议。不论您是AI专家还是科技爱好者,都能找到权威、详尽的测评,帮助您在快速发展的AI领域中做出最佳选择。一起探索AI的真实潜力…...
注意力机制 attention Transformer 笔记
动手学深度学习 这里写自定义目录标题 注意力加性注意力缩放点积注意力多头注意力自注意力Transformer 注意力 注意力汇聚的输出为值的加权和 查询的长度为q,键的长度为k,值的长度为v。 q ∈ 1 q , k ∈ 1 k , v ∈ R 1 v {\bf{q}} \in {^{1 \times…...
开始尝试从0写一个项目--后端(二)
实现学生管理 新增学生 接口设计 请求路径:/admin/student 请求方法:POST 请求参数:请求头:Headers:"Content-Type": "application/json" 请求体:Body: id 学生id …...
【图解大数据技术】Hive、HBase
【图解大数据技术】Hive、HBase Hive数据仓库Hive的执行流程Hive架构数据导入Hive HBaseHBase简介HBase架构HBase的列式存储HBase建表流程HBase数据写入流程HBase数据读取流程 Hive Hive是基于Hadoop的一个数据仓库工具,Hive的数据存储在HDFS上,底层基于…...
composables 目录下的文件(web前端)
composables 目录通常用于存放可组合的函数或逻辑,这些函数或逻辑可以在不同的组件中复用。具体来说,composables 目录下的文件通常包含以下内容: 组合式函数 (Composable Functions): 这些函数利用 Vue 3 的组合式 API࿰…...
使用Python绘制堆积柱形图
使用Python绘制堆积柱形图 堆积柱形图效果代码 堆积柱形图 堆积柱形图(Stacked Bar Chart)是一种数据可视化图表,用于显示不同类别的数值在某一变量上的累积情况。每一个柱状条显示多个子类别的数值,子类别的数值在柱状条上堆积在…...
DP:二维费用背包问题
文章目录 🎵二维费用背包问题🎶引言🎶问题定义🎶动态规划思想🎶状态定义和状态转移方程🎶初始条件和边界情况 🎵例题🎶1.一和零🎶2.盈利计划 🎵总结 …...
C语言标准库中的函数
由于C语言标准库中的函数非常多,我将按类别列出一些常见函数及其作用。请注意,这里不可能列出所有函数,但我会尽量覆盖主要的类别和函数。 ### 标准输入输出 - printf: 格式化输出到标准输出(通常是屏幕)。 - scanf: …...
Qt5.9.9 关于界面拖动导致QModbusRTU(QModbusTCP没有测试过)离线的问题
问题锁定 参考网友的思路: Qt5.9 Modbus request timeout 0x5异常解决 网友认为是Qt的bug, 我也认同;网友认为可以更新模块, 我也认同, 我也编译了Qt5.15.0的code并成功安装到Qt5.9.9中进行使用,界面拖…...
API的定义理解
前言 在程序员的日常工作中,“API”这个词在程序员的口中重复的次数,绝对是名列前茅的。 但是对刚开始工作的新人来说,API这个概念还是比较模糊。 确实,API这个概念是随着语义环境而不一样的,所以会让人迷惑。 下面…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...
