Qt源码解析之QObject

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

//qobject.h
class Q_CORE_EXPORT Qobject{
    Q_OBJECT
    Q_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 QWindow
    uint deleteLaterCalled : 1;
    uint unused : 24;
    int postedEvents;
    QDynamicMetaObjectData *metaObject;
    QMetaObject *dynamicMetaObject() const;
#ifdef QT_DEBUG
    enum { 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 initialization
    q_ptr = nullptr;
    parent = nullptr;                           // no parent yet. It is set by setParent()
    isWidget = false;                           // assume not a widget object
    blockSig = false;                           // not blocking signals
    wasDeleted = false;                         // double-delete catcher
    isDeletingChildren = false;                 // set by deleteChildren()
    sendChildEvents = true;                     // if we should send ChildAdded and ChildRemoved events to parent
    receiveChildEvents = 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 < 0x60000
    qt_addObject(this);
#endif
    if (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_DEBUG
    const 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
    if (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 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);
            }
        }
    }
    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_DEBUG
    const 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) {
    //获取父对象的QObjectPrivate
    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);
        //如果索引为负数,可能表示正在从 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.mfbz.cn/a/773874.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

印章谁在管、谁用了、用在哪?契约锁让您打开手机一看便知

“印章都交给谁在管”、“哪些人能用”、“都有哪些业务在用”…这些既是管理者最关心的印章问题也是影响印章安全的关键要素。但是公司旗下分子公司那么多&#xff0c;各类公章、法人章、财务章、合同章一大堆&#xff0c;想“问”明白很难。 契约锁电子签及印控平台推出“印章…

OpenLayers使用2

接着上一篇https://blog.csdn.net/weixin_51416826/article/details/140161160?spm1001.2014.3001.5502 本篇主要内容是基于高德API逆向地址解析获取城市中心点&#xff0c;并且设置了输入框&#xff0c;可以输入城市执行飞行&#xff0c;同时基于高德API获取城市天气信息&am…

【漏洞复现】万户协同办公平台——反序列化

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 万户协同办公平台ezEIP是一个综合信息基础应用平台&#xff0c;…

Leaflet【六】绘制交互图形、测量、经纬度展示

本文主要探讨了如何利用leaflet-draw插件在地图上绘制图形&#xff0c;以及通过leaflet-measure测量距离和面积&#xff0c;并将经纬度绘制到地图上。首先&#xff0c;我们使用leaflet-draw插件&#xff0c;该插件提供了一种简单而直观的方式来绘制各种形状&#xff08;如点、线…

配置基于不同IP地址的虚拟主机

定义配置文件vhost.conf <directory /www> allowoverride none require all granted </directory> <virtualhost 192.168.209.136:80> documentroot /www servername 192.168.209.136 </virtualhost><virtualhost 192.168.209.138:80> document…

Restore Equipment

Restore Equipment 魔兽世界 - 盗号申请 - 恢复装备流程 魔兽和网易真的不行啊 1&#xff09;这个装备本来就是兑换的竟然可以卖NPC 2&#xff09;针对这个情况竟然无法挽回 3&#xff09;设计理念真的不得不吐槽一下 4&#xff09;策划真的不咋样&#xff0c;要是有机会我要自…

STM32F1+HAL库+FreeTOTS学习5——内核中断管理及中断控制函数

STM32F1HAL库FreeTOTS学习5——中断管理和临界段代码保护 中断简介中断优先级寄存器拓展FreeRTOS中PendSV和Systick中断优先级配置三个中断屏蔽寄存器FreeRTOS中断管理函数代码验证 上一期我们学习了FreeRTOS中任务挂起与恢复&#xff0c;在中断服务程序中恢复任务过程中&#…

Fish Speech: 开源文本转语音技术(TTS)的新里程碑

简介 Fish Speech 是一个全新的文本转语音(TTS)解决方案&#xff0c;该项目由fishaudio开发。当前模型使用约十五万小时三语数据训练&#xff0c;对中文支持非常的完美。 能够熟练处理和生成中文、日语和英语的语音&#xff0c;语言处理能力接近人类水平&#xff0c;并且声音…

狂赚三个亿,百亿医用耗材上市公司重金押注老人轮椅

布局海外市场&#xff0c;轮椅销量翻两番 作者 | 艾米莉 排版 | 张思琪 抛砖引玉 1.年销售60万台轮椅&#xff0c;英科医疗如何做到&#xff1f; 2.老年人轮椅是出海&#xff0c;还是深耕国内市场&#xff1f; 3.2022年全球轮椅市场规模为48亿美元&#xff0c;谁在喝汤&…

Android仿天眼查人物关系图

效果图预览 绘制思路 这里使用了中学解析几何知识 XPoint OPointX OPointXcosθ&#xff1b; YPoint OPointY OPointYsinθ&#xff1b; canvas.drawText(lists.get(i).getName(), XPoint (float) Math.cos(pere * i 5) * radius[i % radius.length] - 30, YPoint (fl…

【笔试记录】腾讯音乐 | 20230903 | cpp (更新ing)

1 完美数 1.1 题目描述 小红定义一个数为“完美数”&#xff0c;当且仅当该数仅有一个非零数字。例如 5000, 4, 1, 10, 200 都是完美数。 小红拿到了一个大小为 n&#xff08;2 < n < 2000&#xff09;的数组 a&#xff0c;她希望选择数组中的两个元素&#xff08;1 …

CVE-2023-30212(xss漏洞)

简介 OURPHP版本<7.2.0存在XSS漏洞&#xff0c;攻击路径为/client/manage/ourphp_out.php。 过程 打开靶场 访问攻击路径/client/manage/ourphp_out.php 得到flag{354c7c41-cc23-4de5-be73-79cbbf384aba}

上海计算机考研炸了,这所学校慎报!上海大学计算机考研考情分析!

上海大学&#xff08;Shanghai University&#xff09;&#xff0c;简称“上大”&#xff0c;是上海市属、国家“211工程”重点建设的综合性大学&#xff0c;教育部与上海市人民政府共建高校&#xff0c;国防科技工业局与上海市人民政府共建高校&#xff0c;国家“双一流”世界…

leetcode--二叉搜索子树的最大键值和

leetcode地址&#xff1a;二叉搜索子树的最大键值和 给你一棵以 root 为根的 二叉树 &#xff0c;请你返回 任意 二叉搜索子树的最大键值和。 二叉搜索树的定义如下&#xff1a; 任意节点的左子树中的键值都 小于 此节点的键值。 任意节点的右子树中的键值都 大于 此节点的键值…

【matlab 路径规划】基于改进遗传粒子群算法的药店配送路径优化

一 背景介绍 本文分享的是一个基于订单合并的订单分配和路径规划联合优化&#xff0c;主要背景是骑手根据客户需求&#xff0c;从药店取药之后进行配送&#xff0c;配送的过程中考虑路径的长度、客户的服务时间窗、车辆的固定成本等要素&#xff0c;经过建模和优化得到最优的配…

什么是声明式编程?发展趋势怎么样的?

一、什么是声明式编程&#xff1f; 声明式编程&#xff08;Declarative programming&#xff09;是一种编程范式&#xff0c;与命令式编程相对立。它主要描述目标的性质&#xff0c;让计算机明白目标&#xff0c;而非具体的执行流程。在声明式编程中&#xff0c;开发者只需声明…

彻底搞懂Kafka生产消费流程,这篇文章就够了!

Hey, 小伙伴们!今天小米给大家带来一篇关于Kafka生产消费基本流程的揭秘,内容超干货!让我们一起揭开Kafka神秘的面纱,探索它的工作原理吧! Producer创建及其内部结构 当我们创建一个Kafka Producer时,Kafka会为我们创建一个叫做Sender的线程,并将其设置为守护线程(Da…

论文解读StyleGAN系列——StyleGANv3

论文&#xff1a;Alias-Free Generative Adversarial Networks&#xff08;2021.06&#xff09; 作者&#xff1a;Tero Karras, Miika Aittala, Samuli Laine, Erik Hrknen, Janne Hellsten, Jaakko Lehtinen, Timo Aila 链接&#xff1a;https://arxiv.org/abs/2106.12423 代码…

计算两个经纬度之间的球面距离(基于Mysql和PHP实现)

计算两个经纬度之间的球面距离 1、MySQL实现方式 - 基于空间函数(ST_Distance_Sphere)实现 前置条件&#xff1a;确保您使用的是 MySQL 8.0 或更高版本&#xff0c;因为较早的版本对地理空间的支持有限。 1.1 创建表和索引 说明&#xff1a;设置 location 为 point 类型 #…

驭码CodeRider将亮相世界人工智能大会,AI 产品、重磅分享,真的很City!

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…