QT 中的元对象系统(二):元对象实现原理QMetaObject

目录

1.元对象系统的构成

2.QObject和QMetaObject的关系

3.Qt 元对象模型QMetaObject

3.1.基本信息

3.2.类信息classinfo

 3.3.类构造函数constructor

3.4.枚举信息 enumerator

3.5.类方法method

3.6.类属性peoproty

4.MOS(Meta Object System)示例

5.总结


1.元对象系统的构成

1) QObject为所有需要利用元对象系统的对象提供一个基类。也就是说只有继承QObject类才能使用MOS。

2) Q_OBJECT宏,在类的声明体内定义在每一个类的私有数据段,用来启用元对象功能,比如,动态属性、信号和槽

3) 元对象编译器moc(Meta Object Compiler),如果一个头文件中包含Q_OBJECT宏定义,moc就会将该文件编译成C++源文件。该原文件包含了Q_OBJECT的实现代码,也被编译,最终链接到这个类的二进制代码中。因为它也是这个类的一部分。通常,这个新的C++原文件会再以前的C++原文件前面加上moc_作为新的文件名。

如下图所示:

4) QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta-object功能,编译器在扫描一个源文件时,如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成该类对应MetaObject类以及对QObject的函数override(重载)。

2.QObject和QMetaObject的关系

        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, "")

        staticMetaObject是静态常量,metaObject()是获取该对象指针的方法。所有QObject的对象都会共享staticMetaObject变量,靠它完成所有信号和槽的功能。        

        QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特有的signal&slot信息。

virtual QObject::metaObject();

        该方法返回一个QObject对应的metaObject对象,如上文所说,如果一个类的声明中包含了Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。

        如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义signal&slot和Property。

        这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。

3.Qt 元对象模型QMetaObject

        笔者环境:win平台vs2019,Qt 版本5.12.12,以下源码分析都是基于这个版本

3.1.基本信息

        Qt 元对象声明位于.\5.12.12\Src\qtbase\src\corelib\kernel\qobjectdefs.h中,位于代码的337行处,我们可以看到其中存放的元数据的结构:

struct Q_CORE_EXPORT QMetaObject
{  const char *className() const;const QMetaObject *superClass() const;。。。struct { // private dataconst QMetaObject *superdata; // 父类的元对象指针const QByteArrayData *stringdata;   // 元数据的字符串数据const uint *data;                  // 元对象的数据信息typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);StaticMetacallFunction static_metacall; // 一个函数指针,信号槽机制会用到const QMetaObject * const *relatedMetaObjects;void *extradata; //reserved for future use} d;
};

        上面的代码就是QMetaObject类所定义的所有数据成员,就是这些数据成员记录了所有的signal、slot、property、class information、enumerator等相关信息。

        const QMetaObject *superdata,该变量指向与之对应的QObject类的父类对象,或者是祖先类的QMetaObject对象。每一个QObject类或者其派生类可能有一个父类或者父类的父类。那么superdata就是指向与其最接近的祖先类中的QMetaObject对象。如果没有父类,那么该变量就是一个NULL指针。

3.2.类信息classinfo

提供额外的类信息-名值对。用户可以在类的生命中以Q_CLASSINFO(name,value)的方式添加。

int classInfoOffset() const;
int classInfoCount() const;
int indexOfClassInfo(const char *name) const;
QMetaClassInfo classInfo(int index) const;

示例如下:

class MyClass : public QObject
{Q_OBJECTQ_CLASSINFO("author", "xiaohe")Q_CLASSINFO("url", "http://www.baidu.com/")public:...
};

 3.3.类构造函数constructor

提供该类的构造方法信息

 int constructorCount() const;int indexOfConstructor(const char *constructor) const;QMetaMethod constructor(int index) const;

3.4.枚举信息 enumerator

描述该类声明体内所包含的枚举类型信息

int enumeratorOffset() const;
int enumeratorCount() const;
int indexOfEnumerator(const char *name) const;
QMetaEnum enumerator(int index) const;

3.5.类方法method

描述类中所包含方法信息:包括property,signal,slot等。

int methodOffset() const;
int methodCount() const;
int indexOfMethod(const char *method) const;
int indexOfSignal(const char *signal) const;
int indexOfSlot(const char *slot) const;
QMetaMethod method(int index) const;

3.6.类属性peoproty

描述类的属性信息

int propertyOffset() const;
int propertyCount() const;
int indexOfProperty(const char *name) const;
QMetaProperty property(int index) const;

注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。

4.MOS(Meta Object System)示例

TestObject继承QObject,定义了两个Property:PropertyA和PropertyB;两个classinfo:Author,version;一个枚举:TestEnum。

CTestObject.h

#pragma once
#include <qobject.h>
class TestObject : public QObject
{Q_OBJECTQ_PROPERTY(QString propertyA  READ getPropertyA WRITE setPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false)Q_PROPERTY(QString propertyB  READ getPropertyB WRITE setPropertyB RESET resetPropertyB)Q_CLASSINFO("Author", "Liuyanghe")Q_CLASSINFO("Version", "TestObjectV1.0")Q_ENUMS(TestEnum)
public:enum TestEnum {EnumValueA,EnumValueB};
public:TestObject() {}Q_INVOKABLE TestObject(const QString& a, const QString& b) : m_A(a), m_B(b) {}QString getPropertyA() const {return m_A;}void setPropertyA(const QString& newValue) {if (newValue == m_A)return;m_A = newValue;emit propertyAChanged();}void resetPropertyA() {m_A = "";}QString getPropertyB() const {return m_B;}void setPropertyB(const QString& newValue) {if (newValue == m_B)return;m_B = newValue;emit propertyBChanged();}void resetPropertyB() {m_B = "";}Q_INVOKABLE int  doWork() {return 1;}
signals:void clicked();void pressed();void propertyAChanged();void propertyBChanged();
public slots:void onEventA(const QString&);void onEventB(int);
private:QString m_A;QString m_B;
};

TestObject的moc文件moc_CTestObject.cpp:

/****************************************************************************
** Meta object code from reading C++ file 'CTestObject.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.12)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include "../../../../CTestObject.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'CTestObject.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.12. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endifQT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_TestObject_t {QByteArrayData data[20];char stringdata0[182];
};
#define QT_MOC_LITERAL(idx, ofs, len) \Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \qptrdiff(offsetof(qt_meta_stringdata_TestObject_t, stringdata0) + ofs \- idx * sizeof(QByteArrayData)) \)
static const qt_meta_stringdata_TestObject_t qt_meta_stringdata_TestObject = {{
QT_MOC_LITERAL(0, 0, 10), // "TestObject"
QT_MOC_LITERAL(1, 11, 6), // "Author"
QT_MOC_LITERAL(2, 18, 9), // "Liuyanghe"
QT_MOC_LITERAL(3, 28, 7), // "Version"
QT_MOC_LITERAL(4, 36, 14), // "TestObjectV1.0"
QT_MOC_LITERAL(5, 51, 7), // "clicked"
QT_MOC_LITERAL(6, 59, 0), // ""
QT_MOC_LITERAL(7, 60, 7), // "pressed"
QT_MOC_LITERAL(8, 68, 16), // "propertyAChanged"
QT_MOC_LITERAL(9, 85, 16), // "propertyBChanged"
QT_MOC_LITERAL(10, 102, 8), // "onEventA"
QT_MOC_LITERAL(11, 111, 8), // "onEventB"
QT_MOC_LITERAL(12, 120, 6), // "doWork"
QT_MOC_LITERAL(13, 127, 1), // "a"
QT_MOC_LITERAL(14, 129, 1), // "b"
QT_MOC_LITERAL(15, 131, 9), // "propertyA"
QT_MOC_LITERAL(16, 141, 9), // "propertyB"
QT_MOC_LITERAL(17, 151, 8), // "TestEnum"
QT_MOC_LITERAL(18, 160, 10), // "EnumValueA"
QT_MOC_LITERAL(19, 171, 10) // "EnumValueB"},"TestObject\0Author\0Liuyanghe\0Version\0""TestObjectV1.0\0clicked\0\0pressed\0""propertyAChanged\0propertyBChanged\0""onEventA\0onEventB\0doWork\0a\0b\0propertyA\0""propertyB\0TestEnum\0EnumValueA\0EnumValueB"
};
#undef QT_MOC_LITERALstatic const uint qt_meta_data_TestObject[] = {// content:8,       // revision0,       // classname2,   14, // classinfo7,   18, // methods2,   69, // properties1,   75, // enums/sets1,   84, // constructors0,       // flags4,       // signalCount// classinfo: key, value1,    2,3,    4,// signals: name, argc, parameters, tag, flags5,    0,   53,    6, 0x06 /* Public */,7,    0,   54,    6, 0x06 /* Public */,8,    0,   55,    6, 0x06 /* Public */,9,    0,   56,    6, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags10,    1,   57,    6, 0x0a /* Public */,11,    1,   60,    6, 0x0a /* Public */,// methods: name, argc, parameters, tag, flags12,    0,   63,    6, 0x02 /* Public */,// signals: parametersQMetaType::Void,QMetaType::Void,QMetaType::Void,QMetaType::Void,// slots: parametersQMetaType::Void, QMetaType::QString,    6,QMetaType::Void, QMetaType::Int,    6,// methods: parametersQMetaType::Int,// constructors: parameters0x80000000 | 6, QMetaType::QString, QMetaType::QString,   13,   14,// properties: name, type, flags15, QMetaType::QString, 0x00095107,16, QMetaType::QString, 0x00095107,// enums: name, alias, flags, count, data17,   17, 0x0,    2,   80,// enum data: key, value18, uint(TestObject::EnumValueA),19, uint(TestObject::EnumValueB),// constructors: name, argc, parameters, tag, flags0,    2,   64,    6, 0x0e /* Public */,0        // eod
};void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::CreateInstance) {switch (_id) {case 0: { TestObject *_r = new TestObject((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< const QString(*)>(_a[2])));if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;default: break;}} else if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<TestObject *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->clicked(); break;case 1: _t->pressed(); break;case 2: _t->propertyAChanged(); break;case 3: _t->propertyBChanged(); break;case 4: _t->onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 5: _t->onEventB((*reinterpret_cast< int(*)>(_a[1]))); break;case 6: { int _r = _t->doWork();if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (TestObject::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::clicked)) {*result = 0;return;}}{using _t = void (TestObject::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::pressed)) {*result = 1;return;}}{using _t = void (TestObject::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::propertyAChanged)) {*result = 2;return;}}{using _t = void (TestObject::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::propertyBChanged)) {*result = 3;return;}}}
#ifndef QT_NO_PROPERTIESelse if (_c == QMetaObject::ReadProperty) {auto *_t = static_cast<TestObject *>(_o);Q_UNUSED(_t)void *_v = _a[0];switch (_id) {case 0: *reinterpret_cast< QString*>(_v) = _t->getPropertyA(); break;case 1: *reinterpret_cast< QString*>(_v) = _t->getPropertyB(); break;default: break;}} else if (_c == QMetaObject::WriteProperty) {auto *_t = static_cast<TestObject *>(_o);Q_UNUSED(_t)void *_v = _a[0];switch (_id) {case 0: _t->setPropertyA(*reinterpret_cast< QString*>(_v)); break;case 1: _t->setPropertyB(*reinterpret_cast< QString*>(_v)); break;default: break;}} else if (_c == QMetaObject::ResetProperty) {TestObject *_t = static_cast<TestObject *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->resetPropertyA(); break;case 1: _t->resetPropertyB(); break;default: break;}}
#endif // QT_NO_PROPERTIES
}QT_INIT_METAOBJECT const QMetaObject TestObject::staticMetaObject = { {&QObject::staticMetaObject,qt_meta_stringdata_TestObject.data,qt_meta_data_TestObject,qt_static_metacall,nullptr,nullptr
} };const QMetaObject *TestObject::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}void *TestObject::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_TestObject.stringdata0))return static_cast<void*>(this);return QObject::qt_metacast(_clname);
}int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QObject::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 7)qt_static_metacall(this, _c, _id, _a);_id -= 7;} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 7)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 7;}
#ifndef QT_NO_PROPERTIESelse if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty|| _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {qt_static_metacall(this, _c, _id, _a);_id -= 2;} else if (_c == QMetaObject::QueryPropertyDesignable) {_id -= 2;} else if (_c == QMetaObject::QueryPropertyScriptable) {_id -= 2;} else if (_c == QMetaObject::QueryPropertyStored) {_id -= 2;} else if (_c == QMetaObject::QueryPropertyEditable) {_id -= 2;} else if (_c == QMetaObject::QueryPropertyUser) {_id -= 2;}
#endif // QT_NO_PROPERTIESreturn _id;
}// SIGNAL 0
void TestObject::clicked()
{QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}// SIGNAL 1
void TestObject::pressed()
{QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}// SIGNAL 2
void TestObject::propertyAChanged()
{QMetaObject::activate(this, &staticMetaObject, 2, nullptr);
}// SIGNAL 3
void TestObject::propertyBChanged()
{QMetaObject::activate(this, &staticMetaObject, 3, nullptr);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

QMetaObjectPrivate 

    1) qt_meta_data_TestObject:定义的正是QMetaObject::d.data指向的信息块。

    2) qt_meta_stringdata_TestObject:定义的是QMetaObject::d.stringdata指向的信息块。

    3) const QMetaObject TestObject::staticMetaObject:定义TestObject类的MetaObject实例,从中可以看出QMetaObject各个字段是如何被赋值的。

    4) const QMetaObject *TestObject::metaObject() const:重写了QObject::metaObject函数,返回上述的MetaObject实例指针。

    5) TestObject::qt_metacall()是重写QObject的方法,依据传入的参数来调用signal&slot或访问property,动态方法调用属性访问正是依赖于这个方法。

    6)TestObject::clicked()和TestObject::pressed()正是对两个signal的实现,可见,signal其实就是一种方法,只不过这种方法由qt meta system来实现,不用我们自己实现。

    TestObject类的所有meta信息就存储在 qt_meta_data_TestObject和qt_meta_stringdata_TestObject这两个静态数据中。 QMetaObject的接口的实现正是基于这两块数据。下面就对这两个数据进行分块说明。

    static const uint qt_meta_data_TestObject[] = { 
           // content:
           8,       // revision
           0,       // classname
           2,   14, // classinfo
           7,   18, // methods
           2,   69, // properties
           1,   75, // enums/sets
           1,   84, // constructors
           0,       // flags
           4,       // signalCount

           //...

    }

            这块数据可以被看做meta信息的头部,正好和QMetaObjectPrivate数据结构相对应,在QMetaObject的实现中,正是将这块数据映射为QMetaObjectPrivate进行使用的。

            QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。该数据结构全是int类型,一些是直接的int型信息,比如classInfoCount、
    methodCount等,还有一些是用于在QMetaObject的stringdata和data内存块中定位信息的索引值。

    struct QMetaObjectPrivate
    {// revision 7 is Qt 5.0 everything lower is not supported// revision 8 is Qt 5.12: It adds the enum name to QMetaEnumenum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbusint revision;int className;int classInfoCount, classInfoData;int methodCount, methodData;int propertyCount, propertyData;int enumeratorCount, enumeratorData;int constructorCount, constructorData;int flags;int signalCount;static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject){ return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); }//...
    };

    第一行数据“8”:版本号;

    第二行数据“0”:类名,该值是qt_meta_stringdata_TestObject的索引,qt_meta_stringdata_TestObject[0](QT_MOC_LITERAL(0, 0, 10), "TestObject" 这个字符串不正是类名“TestObject”吗。

    第三行数据“2,14”,第一个表明有2个classinfo被定义,第二个是说具体的 classinfo信息在qt_meta_data_TestObject中的索引,

    qt_meta_data_TestObject[14]的位置两个 classinfo名值对的定义;这两对键值1和2、3和4分别

    qt_meta_stringdata_TestObject的索引,找到:

    这4个值正是那两个键值对。

    第四行数据“7,18”,指明method的信息,7是指方法的个数;18是指方法(包括信号、槽、Q_INVOKABLE标注的函数)开始的位置:

    信号signals在qt_meta_stringdata_TestObject的索引是5、7、8、9,找到位置如下图所示:

    5,    0,   53,    6, 0x06 /* Public */,是定义信号clicked,其它类似

    槽slots在qt_meta_stringdata_TestObject的索引是10、11,找到位置如下图所示:

    Q_INVOKABLE标注的函数在qt_meta_stringdata_TestObject的索引是12,找到位置如下图所示:

    第五行数据“2,69”,指明属性properties的信息,2是指属性的个数;69是指属性在qt_meta_data_TestObject索引开始的位置:

    开始位置的15和16正是qt_meta_stringdata_TestObject的索引,找到位置正是类属性:

    第六行数据“1,75”,指明enum信息信息,1是指enun信息的个数;75是指enum信息在qt_meta_data_TestObject索引开始的位置:

    开始位置17正是enum名称在qt_meta_data_TestObject索引开始的位置:

    其它分析类似

    第七行数据“1,84”,指明类声明Q_INVOKABLE的构造函数信息,1是指构造函数个数;84是指构造函数在qt_meta_data_TestObject索引开始的位置:

    0,    2,   64,    6, 0x0e /* Public */中的

    0是指构造函数名称在qt_meta_stringdata_TestObject[0]

    2是指这个构造函数的参数为两个

    64是指构造函数的具体参数在qt_meta_data_TestObject[64]:

    上图中的13和14是参数名的位置qt_meta_stringdata_TestObject[13]、qt_meta_stringdata_TestObject[14]:

    static const char qt_meta_stringdata_TestObject[] = {
    //这块数据就是meta信息所需的字符串。是一个字符串的序列。"TestObject\0Author\0Liuyanghe\0Version\0""TestObjectV1.0\0clicked\0\0pressed\0""propertyAChanged\0propertyBChanged\0""onEventA\0onEventB\0doWork\0a\0b\0propertyA\0""propertyB\0TestEnum\0EnumValueA\0EnumValueB"
    };

    5.总结

            Qt 中的元对象系统,简单的可以分为以下几步: 

            1.在继承 QObject 的类中使用 Q_OBJECT 宏,该宏定义了元对象和相关的方法

            2.进行 C++ 编译前,Qt 会运行 moc,解析带有 Q_OBJECT 宏的相关类的信息,生成moc文件,得到元数据并构造元对象

            3.将生成的文件和源文件一起编译

            Qt 元对象系统通过信号与槽机制、运行时类型信息、动态属性系统和国际化支持等功能,极大地增强了 C++ 的功能,使开发者能够更高效地构建复杂的应用程序。它不仅简化了对象间的通信,还提供了强大的运行时反射能力,是 Qt 框架不可或缺的一部分。

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

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

    相关文章

    【新手入门】SQL注入之盲注

    一、引言 在我们的注入语句被带入数据库查询但却什么都没有返回的情况我们该怎么办? 例如应用程序返回到一个"通用的"的页面&#xff0c;或者重定向一个通用页面(可能为网站首页)。这时&#xff0c;我们之前学习的SQL注入的办法就无法使用了。这种情况我们称之为无…

    字段对比清洗

    import pandas as pd import psycopg2 from psycopg2 import sql# 数据库连接配置 DB_CONFIG {"host": "","user": "","password": "","dbname": "","port": , }def get_excel_fi…

    阿里开源正式开园文生视频、图生视频模型-通义万相 WanX2.1

    简介 发布时间与背景 通义万相 Wan2.1 模型于 2025年1月 发布&#xff0c;并迅速登顶视频生成领域权威评测 Vbench 的榜首&#xff0c;超越了包括 Sora、HunyuanVideo、Minimax 等国内外知名模型&#xff0c;并于这周开源。它是阿里云在 AI 视频生成领域的最新成果&#xff0…

    Python的那些事第三十四篇:基于 Plotly 的交互式图表与仪表板设计与应用

    基于 Plotly 的交互式图表与仪表板设计与应用 摘要: 本文深入探讨了 Plotly 这一强大的交互式图表和仪表板库。首先介绍了 Plotly 的背景与发展历程,随后详细阐述了其核心功能特性,包括丰富的图表类型、高度的自定义能力以及便捷的交互操作。通过实际案例分析和示例代码展示…

    英文论文查重,Turnitin和IThenticate两个系统哪个更合适?

    Turnitin系统和IThenticate系统都是检测英文论文的查重系统&#xff0c;但是两者之间还是有一些不一样的。 下面针对这两个系统给大家具体分析一下。 一、Turnitin系统 Turnitin检测系统&#xff1a; https://truth-turnitin.similarity-check.com Turnitin是世界上主流的…

    [Linux]项目自动化构建工具-make/Makefile

    项目自动化构建工具-make/Makefile make与Makefile单文件Makefile多文件Makefile 缓冲区 首先理清多文件之间的关系&#xff1a; 这里为什么没有包含test.h头文件&#xff1f;因为在当前工作目录下&#xff0c;因此不需要包含test.h&#xff0c;如果把test.h移到上一级目录&…

    ArcGIS Pro中打造精美高程渲染图的全面指南

    一、引言 高程渲染图是地理信息系统&#xff08;GIS&#xff09;中用于展示地形地貌的重要工具。一张精美的高程渲染图&#xff0c;不仅能够清晰地呈现地形的起伏变化&#xff0c;还能增强视觉表现力&#xff0c;使得数据更加生动、直观。ArcGIS Pro作为一款强大的GIS软件&…

    ollama本地部署DeepSeek(Window图文说明)

    目录 1. ollama下载2. 环境变量3. deepseek下载4. 彩蛋 1. ollama下载 安装包下载&#xff1a;Window安装包 命令行方式安装&#xff1a;&#xff08;不推荐使用exe方式进行安装&#xff0c;默认会在C盘路径下&#xff09; 点击install之后&#xff1a; 2. 环境变量 先配…

    sqlilab 46 关(布尔、时间盲注)

    sqlilabs 46关&#xff08;布尔、时间盲注&#xff09; 46关有变化了&#xff0c;需要我们输入sort&#xff0c;那我们就从sort1开始 递增测试&#xff1a; 发现测试到sort4就出现报错&#xff1a; 我们查看源码&#xff1a; 从图中可看出&#xff1a;用户输入的sort值被用于查…

    【02】Cocos游戏开发引擎从0开发一款游戏-cocos项目目录结构熟悉-调试运行项目-最重要的assets资源文件认识-场景sense了解-优雅草卓伊凡

    【02】Cocos游戏开发引擎从0开发一款游戏-cocos项目目录结构熟悉-调试运行项目-最重要的assets资源文件认识-场景sense了解-优雅草卓伊凡 开发背景 接下来我们直接打开我们的项目开始进一步操作&#xff0c; 实战开发 导入项目 我把得到的项目解压到本地&#xff0c;我们开…

    spring结合mybatis多租户实现单库分表

    实现单库分表 思路&#xff1a;student表数据量大&#xff0c;所以将其进行分表处理。一共有三个分表&#xff0c;分别是student0&#xff0c;student1&#xff0c;student2&#xff0c;在新增数据的时候&#xff0c;根据请求头中的meta-tenant参数决定数据存在哪张表表。 数…

    数据结构:Top-K问题详解

    一.Top-K问题 #include<stdio.h> //先自主创建n个数据 void CreateNDate() {// 造数据int n 100000;srand(time(0));//表示随时间初始化随机生成数的种子const char* file "data.txt";///创建一个文件FILE* fin fopen(file, "w");//“只写”写入创…

    基于 Flink CDC YAML 的 MySQL 到 Kafka 流式数据集成

    本教程的演示都将在 Flink CDC CLI 中进行&#xff0c;无需一行 Java/Scala 代码&#xff0c;也无需安装 IDE。 这篇教程将展示如何基于 Flink CDC YAML 快速构建 MySQL 到 Kafka 的 Streaming ELT 作业&#xff0c;包含整库同步、表结构变更同步演示和关键参数介绍。 准备阶段…

    AI绘画软件Stable Diffusion详解教程(3):Windows系统本地化部署操作方法(通用版)

    上一篇教程介绍了如何在本地部署Stable Diffusion专业版&#xff0c;虽然便于技术人员研究&#xff0c;但是普通人使用起来不便捷&#xff0c;每次只能通过cmd窗口的指令形式或者python代码方式来画图&#xff0c;要记很多的指令很繁琐。 本篇教程教您搭建webui版的&#xff0…

    进程 ─── linux第10课

    目录 回顾上一节 进程 基本概念 描述进程 - PCB task_struct - PCB的一种 task_ struct内容分类 组织进程 下面来介绍task_struct内部 PID 和PPID 子进程与父进程 getpid()和getppid() 杀进程 exe 和 cwd 回顾上一节 1. 如果我们写的程序要访问硬件,必定通过sy…

    量子计算的数学基础:复数、矩阵和线性代数

    量子计算是基于量子力学原理的一种新型计算模式,它与经典计算机在信息处理的方式上有着根本性的区别。在量子计算中,信息的最小单位是量子比特(qubit),而不是传统计算中的比特。量子比特的状态是通过量子力学中的数学工具来描述的,因此,理解量子计算的数学基础对于深入学…

    PostgreSQL_安装部署

    一、Windows系统下安装 1.下载安装包 登录PostgreSQL: Downloads官网&#xff1a; 选择14.12版本&#xff0c;点击下载&#xff1a; 2.安装PostgrSQL14.12 双击exe安装包程序&#xff0c;准备安装&#xff1a; 选择安装路径&#xff1a; 选择想安装的工具&#xff1a; 选择数…

    Idea 和 Pycharm 快捷键

    一、快捷键 二、Pycharm 中怎么切换分支 参考如下 如果在界面右下角 没有看到当前所在的分支&#xff0c;如 “Git:master” 3. 有了 4.

    第十四届蓝桥杯:DFS之飞机降落

    这道题&#xff0c;由于它的数据范围是非常小的&#xff0c;我们可以采取暴力搜索的措施&#xff0c;把每种情况都枚举出来&#xff0c;如果有能行的情况就返回true 同时我们也要学会剪枝&#xff0c;如果已经确认飞机不能降落&#xff0c;就不要往下再展开了 #include <i…

    Oracle 查询表空间使用情况及收缩数据文件

    本文介绍Oracle收缩数据文件的相关操作&#xff0c;运维工作中有时会需要通过收缩数据文件来释放磁盘空间。 数据文件初始化方式&#xff1a; 1.我们创建表空间一般有两种方式初始化其数据文件&#xff0c;即指定初始大小为32G&#xff08;很大的值&#xff09;或指定初始大小为…