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 框架不可或缺的一部分。
相关文章:
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.元对象系…...
C++:指针函数与函数指针
文章目录 概述什么是指针函数(Pointer Function)什么是函数指针(Function Pointer)总结: 概述 什么是指针函数(Pointer Function) 指针函数是指返回类型为指针的函数。即,函数返回…...
用HTML5+CSS+JavaScript实现新奇挂钟动画
用HTML5+CSS+JavaScript实现新奇挂钟动画 引言 在技术博客中,如何吸引粉丝并保持他们的关注?除了干货内容,独特的视觉效果也是关键。今天,我们将通过HTML5、CSS和JavaScript实现一个新奇挂钟动画,并将其嵌入到你的网站中。这个动画不仅能让你的网站脱颖而出,还能展示你的…...
【MySQL篇】表的操作
1,创建表 语法: create table ( field1 datatype, field2 datatype, field3 datatype )charset 字符集 collate 校验规则 engine 存储引擎; 说明: field表示列名datatype表示列的类型charset字符集,如果没有指明,则…...
CCF-CSP历年真题大全附题解python
2023012的真题 202312-1 仓库规划 5415. 仓库规划 - AcWing题库高质量的算法题库https://www.acwing.com/problem/content/5418/ 解题思路: 其实就是对比(x.y,z.....)需要找到一个每个元素都大于这个坐标得坐标,本题可以直接…...
Mysql——约束与多表查询
一、约束 1.1定义 约束是对表中的数据进行限制的一套规则,用于防止用户向数据库中输入无效数据。它可以保证表中的数据满足特定业务规则和逻辑,从而维护数据的准确性和可靠性。 1.2作用 数据完整性 :约束可以确保数据在插入、更新或删除时符…...
如何看待 Kaiming He 最新提出的 Fractal Generative Models ?
何恺明团队提出的分形生成模型(Fractal Generative Models) 引发了广泛关注,其核心思想是通过递归调用生成模型模块构建自相似结构,类似数学中的分形概念(如雪花结构),从而高效生成高分辨率数据(如图像)。 Fractal Generative Models即分形生成模型,是一种新型的生成…...
【大模型系列篇】如何解决DeepSeek-R1结构化输出问题,使用PydanticAl和DeepSeek构建结构化Agent
今日号外:🔥🔥🔥 DeepSeek开源周:炸场!DeepSeek开源FlashMLA,提升GPU效率 下面我们开始今天的主题,deepseek官方明确表示deepseek-r1目前不支持json输出/function call,可…...
Metal 学习笔记五:3D变换
在上一章中,您通过在 vertex 函数中计算position,来平移顶点和在屏幕上移动对象。但是,在 3D 空间中,您还想执行更多操作,例如旋转和缩放对象。您还需要一个场景内摄像机,以便您可以在场景中移动。 要移动…...
Android-创建mipmap-anydpi-v26的Logo
利用 Android Studio 自动创建 创建新项目:打开 Android Studio,点击 “Start a new Android Studio project” 创建新项目。在创建项目的过程中,当设置Target SDK Version为 26 或更高版本时,Android Studio 会在项目的res目录下…...
DOM 事件 HTML 标签属性速查手册
以下是一份 DOM 事件 & HTML 标签属性速查手册,涵盖常用场景和示例,助你快速查阅和使用: 一、DOM 事件速查表 1. 鼠标事件 事件名触发时机适用元素示例代码click元素被点击任意可见元素button.addEventListener(click, () > { ... …...
差旅费控平台作用、功能、11款主流产品优劣势对比
本文将对比以下11款主流费控系统:合思、喜报销、泛微费控报销系统、经贝管家、每刻报销、SAP Concur、Expensify、Zoho Expense等。 随着全球化和企业跨地区运营的不断发展,企业差旅管理已逐渐成为许多公司面临的管理挑战之一。从差旅申请到费用报销的全…...
常用的配置文件格式对比(ini,toml,yaml,json,env,settings.py)及应用程序修改自身配置并保留注释
代码与环境配置解耦 git分支的代码应做到“环境无关”:代码本身不硬编码任何环境特定的配置(如数据库连接、密钥、API地址),而是通过外部机制动态注入。 配置与代码分离:将配置信息存储在代码库之外(如环…...
MySQL 存储过程详解
文章目录 1. 存储过程定义1.1 基本概念1.2 核心特点1.3 存储过程 vs 函数 2. 工作原理与示意图2.1 执行流程2.2 示意图 3. 使用场景3.1 复杂业务逻辑3.2 批量数据处理3.3 权限控制3.4 性能优化 4. 示例与说明4.1 基础示例:创建存储过程4.2 带输出参数的存储过程4.3 …...
使用DeepSeek/chatgpt等AI工具辅助网络协议流量数据包分析
随着deepseek,chatgpt等大模型的能力越来越强大,本文将介绍一下deepseek等LLM在分数流量数据包这方面的能力。为需要借助LLM等大模型辅助分析流量数据包的同学提供参考,也了解一下目前是否有必要继续学习wireshark工具以及复杂的协议知识。 pcap格式 目…...
源码压缩包泄露
##解题思路 因为网站的文件都放在www下面,所以直接访问/www.zip就可以得到网页的源码压缩包 在fl000g.txt这个文件中看到一个flag{flag_here}不像是真的flag,尝试提交ctfshow{flag_here},果然提交失败 打开文件属性之类的,也没有…...
TCP/IP 5层协议簇:数据链路层(交换机工作原理)
目录 1. 数据链路层 2. 帧 3. 工作在数据链路层的设备 4. 交换机工作原理 1. 数据链路层 数据链路层(2层Data Link Layer):传输单元是帧、这层工作的主要设备二层交换机、网卡 2. 帧 帧的结构如下:帧最大1518字节 如下&…...
TCP/IP的分层结构、各层的典型协议,以及与ISO七层模型的差别
1. TCP/IP的分层结构 TCP/IP模型是一个四层模型,主要用于网络通信的设计和实现。它的分层结构如下: (1) 应用层(Application Layer) 功能:提供应用程序之间的通信服务,处理特定的应用细节。 典型协议&am…...
【分布式理论11】分布式协同之分布式事务(一个应用操作多个资源):从刚性事务到柔性事务的演进
文章目录 一. 什么是分布式事务?二. 分布式事务的挑战三. 事务的ACID特性四. CAP理论与BASE理论1. CAP理论1.1. 三大特性1.2. 三者不能兼得 2. BASE理论 五. 分布式事务解决方案1. 两阶段提交(2PC)2. TCC(Try-Confirm-Cancel&…...
Linux: 已占用接口
Linux: 已占用接口 1. netstat(适用于旧系统)1.1 书中对该命令的介绍 2. ss(适用于新系统,替代 netstat)3. lsof(查看详细进程信息)4. fuser(快速查找占用端口的进程)5. …...
HWUI 和 Skia
📌 HWUI 和 Skia 的关系 Skia 是 Android 的底层 2D 图形库,提供 CPU 和 GPU 渲染能力,支持 OpenGL、Vulkan、Metal 等后端。HWUI 是 Android UI 组件的 GPU 渲染引擎,主要用于 加速 View、动画、阴影等 UI 元素的绘制。HWUI 依赖…...
Pytorch使用手册--将 PyTorch 模型导出为 ONNX(专题二十六)
注意 截至 PyTorch 2.1,ONNX 导出器有两个版本。 torch.onnx.dynamo_export 是最新的(仍处于测试阶段)导出器,基于 PyTorch 2.0 发布的 TorchDynamo 技术。 torch.onnx.export 基于 TorchScript 后端,自 PyTorch 1.2.0 起可用。 一、torch.onnx.dynamo_export使用 在 60 …...
GitHub SSH连接问题解决指南
🔍 GitHub SSH连接问题解决指南 问题描述 遇到错误:ssh: connect to host github.com port 22: Connection refused 说明您的网络环境无法访问GitHub的SSH端口22,常见原因: 防火墙/网络运营商限制(国内常见…...
【Linux】vim 设置
【Linux】vim 设置 零、起因 刚学Linux,有时候会重装Linux系统,然后默认的vi不太好用,需要进行一些设置,本文简述如何配置一个好用的vim。 壹、软件安装 sudo apt-get install vim贰、配置路径 对所有用户生效: …...
2025届开发岗秋招经验教训总结
实习 实习的作用: 学习知识,充实简历了解特定部门的氛围转正比秋招提前占坑提前展现工作能力,争取更高起薪 其中,除非有梦中情组,我认为占坑是最不重要的一个,因为现在的校招就业形势并未严峻到转正占满…...
Java 之集成 DataX 数据同步工具
1、官网下载 DataX https://github.com/alibaba/DataX 2、将依赖添加到本地(DataX没有maven坐标,需要自己安装) mvn install:install-file -Dfile"datax-common-0.0.1.jar" "-DgroupIdcom.datax" "-DartifactIdda…...
JMH 详细使用
JMH 基本使用 官方地址 JMH 是一个用于 Java 代码微基准测试的工具,允许开发者对特定部分进行精确的性能测试; 安装 JMH 环境: Maven 项目;JDK 8; Maven 方式安装 引入如下依赖: <dependencies>…...
2025 PHP授权系统网站源码
2025 PHP授权系统网站源码 安装教程: PHP7.0以上 先上传源码到服务器,然后再配置伪静态, 访问域名根据操作完成安装, 然后配置伪静态规则。 Ngix伪静态规则: location / { if (!-e $request_filename) { rewrite …...
【对话推荐系统】Towards Topic-Guided Conversational Recommender System 论文阅读
Towards Topic-Guided Conversational Recommender System 论文阅读 Abstract1 Introduction2 Related Work2.1 Conversation System2.2 Conversational Recommender System2.3 Dataset for Conversational Recommendation 3 Dataset Construction3.1 Collecting Movies for Re…...
git 使用常见错误整理
1. git am 应用补丁时遇到错误 fatal: previous rebase directory .git/rebase-apply still exists but mbox given fatal:之前的变基目录 .git/rebase-apply仍然存在,但却提供了mbox 答:这通常是因为之前的 git am 或 git rebase 操作失败后…...
数据存储:一文掌握存储数据到mysql的详细使用
文章目录 一、环境准备1.1 安装MySQL数据库1.2 安装Python MySQL驱动 二、连接到MySQL数据库三、执行基本的CRUD操作3.1 创建(Create):插入数据3.2 读取(Read):查询数据3.3 更新(Update…...
React 源码揭秘 | 更新队列
前面几篇遇到updateQueue的时候,我们把它先简单的当成了一个队列处理,这篇我们来详细讨论一下这个更新队列。 有关updateQueue中的部分,可以见源码 UpdateQueue实现 Update对象 我们先来看一下UpdateQueue中的内容,Update对象&…...
Vue3 + Vite + TS,使用 配置项目别名属性:resolve
使用 resolve 配置全局项目路径别名 1.优化了开发中单页面引用其他模块的路径复杂性 2.妥妥解决了,组件复用当中提高开发效率 // 不使用配置 import { useStore } from ../../../stores // 使用配置 可根据开发者需求任意定义,较多 import { useStore…...
使用 C# 以api的形式调用 DeepSeek
一:创建 API 密钥 首先,您需要来自 DeepSeek 的 API 密钥。访问 DeepSeek,创建一个帐户,并生成一个新的 API 密钥。 二:安装所需的 NuGet 包 使用 NuGet 包管理器安装包,或在包管理器控制台中运行以下命…...
手机放兜里,支付宝“碰一下”被盗刷?
大家好,我是小悟。 近期,网络上关于“支付宝‘碰一下’支付易被盗刷”的传言甚嚣尘上,不少用户对此心生疑虑。 首先,要明确一点:“碰一下”支付并不会像某些传言中所描述的那样容易被隔空盗刷。这一观点已经得到了支付…...
0x01 html和css
css 对于三种css使用方式: 第一种:行内样式 <span style"color: grey;">2024年05月15日 20:07</span>第二种:内部样式 <!DOCTYPE html> <html lang"en"> <head>...<style>span{…...
模型和数据集的平台之在Hugging Face上进行模型下载、上传以及创建专属Space
模型下载 步骤: 注册Hugging Face平台 https://huggingface.co/ 新建一个hf_download_josn.py 文件 touch hf_download_josn.py 编写hf_download_josn.py文件 import os from huggingface_hub import hf_hub_download# 指定模型标识符 repo_id "inter…...
轮式机器人在复杂地形中如何选择合适的全局路径规划算法?
已思考完成 收起 嗯,用户问的是轮式机器人在复杂地形中如何选择合适的全局路径规划算法。首先,我需要理解复杂地形可能带来的挑战,比如崎岖的路面、动态障碍物、非结构化环境等等。轮式机器人在这里的运动控制需要考虑地形通过性、稳定性&…...
免费使用 DeepSeek API 教程及资源汇总
免费使用 DeepSeek API 教程及资源汇总 一、DeepSeek API 资源汇总1.1 火山引擎1.2 百度千帆1.3 阿里百炼1.4 腾讯云 二、其他平台2.1 华为云2.2 硅基流动 三、总结 DeepSeek-R1 作为 2025 年初发布的推理大模型,凭借其卓越的逻辑推理能力和成本优势,迅速…...
文献汇总|AI生成图像检测相关数据集汇总
前言:本博客汇总当前AI生成图像检测领域用到的数据集及相关链接。 ⚠️:除标注「未公开」数据集,其余数据集均已开源。 目录 2020202220232024 2020 CNNSpot https://github.com/peterwang512/CNNDetection Testset: The zip file contains …...
C# 弃元的使用
总目录 前言 在C# 7.0及更高版本中,弃元(Discard)是一个新的语言特性,允许开发者在特定情况下忽略某些值。弃元用下划线 _ 作为占位符,明确表示忽略某个值,提升代码可读性 一、弃元是什么? 1.…...
蓝桥杯备考:贪心算法之矩阵消除游戏
这道题是牛客上的一道题,它呢和我们之前的排座位游戏非常之相似,但是,排座位问题选择行和列是不会改变元素的值的,这道题呢每每选一行都会把这行或者这列清零,所以我们的策略就是先用二进制把选择所有行的情况全部枚举…...
React面试(一)
文章目录 1.vue和react有什么异同2.useEffect中为什么不能使用异步3.useEffect和useLayoutEffect的区别4.react的生命周期5.state和prop的区别6.受控组件和非受控组件7.为什么react16之后不把事件挂载到document上了8.讲一下react的hoc,它可以用来做什么?…...
《解锁AI密码,机器人精准感知环境不再是梦!》
在科技飞速发展的当下,人工智能与机器人技术的融合正深刻改变着世界。其中,人工智能助力机器人实现更精准的环境感知,已成为该领域的核心课题,吸引着全球科研人员与科技企业的目光。这不仅关乎机器人能否在复杂环境中高效执行任务…...
C/C++语言知识点二
1. 编程算法之“哨兵”思想 哨兵思想是一种编程技巧,通过在数据结构的边界或特定位置放置一个特殊值(称为“哨兵”),来简化逻辑判断和提高代码效率。哨兵通常是一个标记值,用于指示某种条件或边界,从而避免…...
【SpringBoot】——分组校验、自定义注解、登入验证(集成redis)、属性配置方式、多环境开发系统学习知识
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
【EB-03】 AUTOSAR builder与EB RTE集成
AUTOSAR builder与EB RTE集成 1. Import Arxml files to Tresos2. Run MultiTask Script3. Add Components3.1 Run EcuExtractCreator Script4. Mapping Component to Partitions5. Event Mapping/Runnables Mapping to Tasks6. Port Connect7. Run SvcAs_Trigger Script8. Ver…...
布署elfk-准备工作
建议申请5台机器部署elfk: filebeat(每台app)--> logstash(2台keepalived)--> elasticsearch(3台)--> kibana(部署es上)采集输出 处理转发 分布式存储 展示 ELK中文社区: 搜索客,搜索人自己的社区 官方…...
JVM垃圾回收器深度底层原理分析与知识体系构建
一、垃圾回收的基本步骤 标记(Marking) 从GC Roots(如虚拟机栈、方法区静态变量、本地方法栈等)出发,遍历对象引用链,标记所有可达对象为存活对象,未被标记的则视为垃圾。此阶段需暂停用户线程&…...
Flutter系列教程之(5)——常用控件Widget的使用示例
目录 1.页面跳转 2.某个控件设置点击事件 3.AlertDialog对话框的使用 4.文本输入框 5.按钮 圆角扁平按钮: 圆角悬浮按钮: 6.补充 圆点 7.布局使用 Row控件左右对齐 调整边距 1.页面跳转 首先,先介绍一下页面跳转功能吧 Flutter使用 Navigator 进行页面…...