【QT】元对象系统学习笔记(一)

QT元对象系统

      • 01、元对象系统
        • 1.1、 元对象运行原则
        • 1.2、 Q_OBJECT宏
        • 1.3、 Qt Creator启动元对象系统
        • 1.4、 命令行启动元对象(不常用)
      • 02、反射机制
        • 2.1、 Qt实现反射机制
        • 2.2、 反射机制获取类中成员函数的信息
          • 2.1.1、 QMetaMethon类
          • 2.1.2、QMetaObject类
        • 2.3、 反射机制获取类相关的信息

01、元对象系统

元对象系统是Qt对原有的C++进行的一些扩展,主要是为了实现信号和槽机制而引入的,信号和槽机制是Qt的核心特性。

Qt的元对象系统提供的功能有:
1、对象间通信的信号和槽机制
2、运行时类型信息和动态属性系统等

要使用元对象系统的功能,需要满足三个前置条件。

  1. 该类必须继承自QObject类。
  2. 必须在类申明的私有区域添加Q_OBJECT宏,该宏用于启动元对象特性,然后就可以使用动态特性、信号和槽等功能。
  3. 元对象编译器(moc)为每个QObject的子类提供实现了元对象特性所必须得代码。

1.1、 元对象运行原则

  • 因为元对象系统是对C++的扩展,因此使用传统的编译器是不能直接编译启用了元对象系统的Qt程序的,对此在编译Qt程序之前,需要把扩展的语法去掉,该功能就是moc要做的事情。
  • moc的全称是:Meta-Object Compiler(元对象编译器),它是一个工具(类似于qmake),该工具读取并分析C++源文件,若发现一个或多个包含了Q_OBJECT宏的类的申明,则会生成另外一个包含了Q_OBJECT宏实现代码的C++源文件(该源文件通常名称为:moc_*.cpp),这个新的源文件要么被#include包含到类 的源文件中,要么被编译键解到类的实现中(通常使用这种方法)。注意:新文件不会“替换”掉旧的文件,而是同源文件一起编译。

其他概念 :

1、元对象代码:指的是moc工具生成的源文件的代码,其中包含有Q_OBJECT宏的实现代码
2、moc工具的路径一般在安装路径下,如:C:\app\Qt5.8.0MinGw\5.8\mingw53_32\bin

1.2、 Q_OBJECT宏

先看一张图片,只要包含了Q_OBJECT宏,然后F2,就能进入。
在这里插入图片描述
可见,Q_OBJECT宏为申明的类之中增加了一些成员,而且红框标注的是虚函数成员(注意这些虚函数没有定义),按照C++语法,虚函数必须定义或者被申明为纯虚函数,moc工具的工作之一就是生成以上成员的定义,并且还会生成一些其他必要的代码。

1.3、 Qt Creator启动元对象系统

由于moc工具是通过Qt Creator来使用的,因此必须保证moc能发现并处理项目中包含有Q_OBJECT宏的类,所以,需要遵守以下规则:
1、从QObject派生的含有Q_OBJECT宏的类的定义必须在头文件中;
2、确保pro文件中,是否列举了项目中的所有源文件(SOURCES变量)和头文件(HEADERS变量);
3、应在头文件中使用逻辑指令如(#ifndef、#define、#endif)防止头文件重复包含多次。
4、QObject类应是基类列表中的第一个类。

由以上规则可见,使用 Qt Creator 编写代码时,类应定义在头文件中,成员函数
的定义应位于源文件中(这样可避免头文件被包含多次产生的重定义错误),虽然
这样编写程序比较麻烦,但这是一种良好的代码组织方式。

注意:如果定义了QObject类的派生类,并进行了构建(构建未添加O_OBJECT,而是在构建完成之后才添加),则此时必须再执行一次qmake命令,否则moc不能生成代码。

这里贴上书中的原话:
在这里插入图片描述
运行以上程序,可在 debug 目录下找到一个 moc_m.cpp 的源文件,该源文件就是使用 moc
工具生成的,该源文件中的代码就是元对象代码,读者可查看其代码。若在该目录没有
moc_m.cpp 文件,说明 moc 工具未能正常启动,这时需在 Qt Creator 中执行 qmake 命令,
再构建程序。

1.4、 命令行启动元对象(不常用)

  1. 在命令行需使用 moc 工具,并在源文件中包含 moc 工具生成的 cpp 文件。
  2. 此时包含 Q_OBJECT 的类不需要位于头文件中,假设位于 m.cpp 文件内,内容为:
#include<QObject>
class A : public QObject
{  
	Q_OBJECT
public:
  A(){}  
};

int main(int argc, char *argv[]) {
 	A ma; return 0; 
}
  1. 打开 Qt 5.8 for Desktop (MinGW 5.3.0 32 bit)命令行工具,输入如下命令:
moc d:\qt\m.cpp -o d:\qt\mm.cpp

以上命令会根据 m.cpp 生成 mm.cpp 文件,mm.cpp 文件中的代码就是元对象代码,此处
m.cpp 和 mm.cpp 都位于 d:\qt 文件夹下。

  1. 然后再次打开 m.cpp,在其中使用#include 把 mm.cpp 包含进去,如下:
#include <QObject>
//#include "mm.cpp"   // 不能把mm.cpp包含在类A的定义之前
class A : public QObject {
	Q_OBJECT
public:
	A(){}
};

#include "mm.cpp"  // 必须把mm.cpp包含在类A的定义后面,因为mm.cpp源文件中有对A的成员定义,此时必须见到类A的完整定义。

int main(int argc, char* argv[]) {
	A ma;
	return 0;
}
  1. 然后再使用 qmake 生成项目文件和 makefile 文件,再使用 mingw32-make 命令即可。

命令行这种方式,一般来说QT开发者是很少用到,因为Qt Creator已经帮我们做了这件事,这里只是看见书上有讲,记录一下。

02、反射机制

reflection模式(反射模式/反射机制):是指在运行时,能获取任意一个类对象的所有类型信息、属性、成员函数等信息的一种机制。

元对象系统与反射机制:
1、元对象系统提供的功能之一是为 QObject 派生类对象提供运行时的类型信息及数据成
员的当前值等信息,也就是说,在运行阶段,程序可以获取 QObject 派生类对象所属
类的名称、父类名称、该对象的成员函数、枚举类型、数据成员等信息,其实这就是
反射机制。

2、因为 Qt 的元对象系统必须从 QObject 继承,又从反射机制的主要作用可看到,Qt 的
元对象系统主要是为程序提供了 QObject 类对象及其派生类对象的信息,也就是说不
是从 QObject 派生的类对象,则无法使用 Qt 的元对象系统来获取这些信息。

元对象的具体解释:是指用于描述另一个对象结构的对象。

具象化到程序上如下:

class B
{
	//TODO....
};

class A : public B
{
	B mb;
};

假设mb是用来描述类A创建的对象的,则mb就是元对象。

2.1、 Qt实现反射机制

  1. Qt使用了一系列的类来实现反射机制,这些类对对象的各个反面进行了描述,其中QMetaObject类描述了QObject及其派生类对象的所有元信息,该类是Qt元对象系统的核心类,通过该类的成员函数可以获取QObject及其派生类对象的所有元信息,因此可以说QMetaObject类的对象是Qt中的元对象。 注意:要调用QMetaObject类中的成员函数需要使用QMetaObject类型的对象。
  2. 对对象的成员进行描述:一个对象包含数据成员、函数成员、构造函数、枚举成员等,在Qt中,这些成员分别使用了不同的类对其进行描述。比如函数成员使用类QMetaMethod、属性使用QMetaProperty进行描述等。最后使用QMetaObject类对整个类对象进行描述,比如要获取成员函数的变量名:
QMetaMethod qm = metaObject->method(1);  // 获取索引为1的成员函数
qDebug()<< qm.name() << "\n";  // 输出该成员函数的名称

使用Qt反射机制的前置条件:

  1. 需要继承自QObject类,并需要在类之中加入Q_OBJECT宏。
  2. 注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入QObject::Q_INVOKABLE宏。
  3. 注册成员变量:若希望成员变量能够被反射,需要使用Q_PROPERTY宏。

其实关于QObject::Q_INVOKABLEQ_PROPERTY这两个宏,在Qt5中的QQuick中就有很深切的体会,QML要使用C++这边的属性、方法都是需要注明这个宏的。

Qt 反射机制实现原理简述:

  1. Q_OBJECT宏展开之后有一个虚拟成员函数meteObject(),该函数会返回一个指向QMetaObject类型的指针,其原型为:
virtual const QMetaObject* metaObject() const;

因为启动了元对象系统的类都包含了Q_OBJECT宏,所以这些类都含有metaObject()虚拟成员函数,通过该函数返回的指针调用QMetaObject类中的成员函数,便可以查询到QObject及其派生类对象的各种信息。

  1. Qt的moc会完成以下工作:
    1、为Q_OBJECT宏展开后所声明的成员函数的生成实现代码;
    2、识别Qt中特殊的关键字及宏,比如识别出Q_PROPRTYQ_INVOKABLEslotsignals等。

2.2、 反射机制获取类中成员函数的信息

2.1.1、 QMetaMethon类

作用:用于描述对象的成员函数,可使用该类的成员函数获取对象成员函数的信息。

列举一些常用的成员:

// 此枚举用于描述函数的类型,即:普通成员函数、信号、槽、构造函数
enum MethodType {Method, Signal, Slot, Constructor}

// 此枚举主要用于描述函数的访问级别(私有的、受保护的、公有的)
enum Access {Private,Protected,Public}

// 返回函数的签名(qt5.0)
QByteArray methodSignature() const;

// 返回函数的类型(信号、槽、成员函数、构造函数)
MethodType methodType() const;

// 返回函数的名称(qt5.0)
QByteArray name() const;

// 返回函数的参数数量(qt5.0)
int parameterCount() const;

// 返回函数参数名称的列表
QList<QByteArray> parameterNames() const;

// 返回指定索引处的参数类型,返回值是使用QMetaType注册的类型,若类型未注册,则返回值为QMetaType::UnknownType。
int parameterType(int index) const;

// 返回函数参数类型的列表
QList<QByteArray> parameterTypes() const;

// 返回函数的返回类型。返回值是使用QMetaType注册的类型,若类型未注册,则返回值为QMetaType::UnknownType。
int returnType() const;

// 返回函数的返回类型的名称
const char* typeName() const;

// 返回函数的访问级别(私有的、受保护的、公有的)
Access access() const;
2.1.2、QMetaObject类

作用:用于提供关于类的元对象信息。

列举一些常用的成员:

/* 返回名为 f 的函数的索引号,否则返回-1。此处应输入正确的函数签名,比如函数形
式为 void f(int i,int j);则正确的形式为 xx.indexOfMethod("f(int,int"); 以下形式都不是
正确的形式,"f(int i, int j)"、"void f(int, int)"、 "f"、"void f"等。*/
int indexOfMethod(const char* f) const;

// 返回信号 s 的索引号,否则返回-1,若指定的函数存在,但不是信号,仍返回-1。
int indexOfSignal(const char * s) const;

// 返回构造函数 c 的索引号,否则返回-1
int indexOfConstructor(const char *c) const;

// 返回构造函数的数量。
int constructorCount() const ; 

// 返回指定索引 i 处的构造函数的元数据。
QMetaMethod constructor(int i)const;

// 返回函数的数量,包括基类中的函数、信号、槽和普通成员函数。
int methodCount() const;

// 返回父类中的所有函数的总和,也就是说返回的值是该类中的第一个成员函数的索引位置。
int methodOffset() const;

// 返回指定索引 i 处的函数的元数据。
QMetaMethod method(int i) const;

下面是书上的一个示例,仅供参考

#include "m.h"
#include <QMetaMethod>
#include <QByteArray>
#include <iostream>
using namespace std;
int main(){  A ma; B mb; //创建两个对象
const QMetaObject *pa=ma. metaObject ();
const QMetaObject *pb=mb. metaObject ();
//以下为通过 QMetaObject 的成员函数获取的信息。
int j=pa->methodCount(); /*返回对象 ma 中的成员函数数量,包括从父类 QObject 继承而来的 5 个
成员函数及本对象中的 2 个成员函数(注意,不包括 g1)、1 个信号,所以
总数为 8。*/
cout<<j<<endl; //输出 8
int i=pa->indexOfMethod("g(int,float)"); //获取对象 ma 中的成员函数 g 的索引号。
cout<<i<<endl; //输出 7
i=pa->constructorCount(); //获取对象 ma 所属类中的构造函数的数量。
cout<<i<<endl; //输出 2
i=pb->constructorCount(); /*获取对象 mb 所属类 B 中的构造函数的数量,因类 B 无构造函数,所以
返回值为 0,此处也可看到,构造函数数量不包含父类的构造函数*/
cout<<i<<endl; //输出 0。
i=pa->indexOfConstructor("A(int)"); //获取对象 ma 所属类中的构造函数 A(int)的索引号。
cout<<i<<endl; //输出 1。
i=pa->indexOfSignal("gb3()"); //获取对象 ma 的信号 gb3()的索引号。
cout<<i<<endl; //输出 5。
i=pa->indexOfSignal("f()"); /*获取对象 ma 的信号 f()的索引号。因为成员函数 f 存在,但不是信
号,所以返回值为-1。*/
cout<<i<<endl; //输出-1。
i=pb->methodOffset(); /*获取父类的成员函数数量,包括父类A及QObject中的成员函数,总共为8。
*/
cout<<i<<endl; //输出 8,此处相当于是对象 mb 自身成员函数开始处的索引号。
//以下为通过 QMetaMethon 的成员函数获取的信息。
//获取对象 ma 的成员函数 g 的元数据。
QMetaMethod m=pa->method(pa->indexOfMethod("g(int,float)"));
QByteArray s= m.name(); //获取成员函数 g 的函数名。
cout<<s.data()<<endl; //输出 g
s=m.methodSignature(); //获取函数 g 的签名
cout<<s.data()<<endl; //输出 g(int,float)
i=m.methodType(); /*获取函数 g 的类型,此处返回的是 QMetaMethod::MethodType 中定义的枚举值,
其中 Method=0,表示类型为成员函数*/
cout<<i<<endl; //输出 0(表示成员函数)。
//以下信息与函数的返回类型有关
s=m.typeName(); //获取函数 g 的返回值的类型名
cout<<s.data()<<endl; //输出 void
i=m.returnType(); /*获取函数 g 返回值的类型,此处的类型是 QMetaType 中定义的枚举值,其中枚举
值 QMetaType::void=43*/
cout<<i<<endl; //输出 43
//以下信息与函数的参数有关
i=m.parameterType(1); /*获取函数 g 中索引号为 1 的参数类型,此处的类型是 QMetaType 中定义的
枚举值,其中枚举值 QMetaType::float=38*/
cout<<i<<endl; //输出 38
QList<QByteArray> q=m.parameterNames(); //获取函数 g 的参数名列表
cout<<q[0].data()<<q[1].data()<<endl; //输出 ij
q=m.parameterTypes(); //获取函数 g 的参数类型列表。
cout<<q[0].data()<<q[1].data()<<endl; //输出 intfloat
return 0; }

2.3、 反射机制获取类相关的信息

  1. QMetaObject类中获取与类相关的信息的成员函数有:
/*获取类的名称,注意,若某个 QObject 的子类未启动元对象系统(即未使用 Q_OBJECT
宏),则该函数将获取与该类最接近的启动了元对象系统的父类的名称,而不再返回
该类的名称,因此建议所有的 QObject 子类都使用 Q_OBJECT 宏。
*/
const char* className() const;

// 返回父类的元对象,若没有这样的对象则返回0
const QMetaObject* superClass() const;

// 若该类继承自mo描述的类型,则返回true,否则返回false。类被认为继承自身。
bool inherits(const QMetaObject* mo) const;  // (Qt5.7)
  1. QObject类中获取与类相关的信息的成员函数有:
// 若该类是className指定的类的子类则返回true,否则返回false。类被认为继承自身
bool inherits(const char* className) const;

下面是一个示例:

头文件:

#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
class A:public QObject{ Q_OBJECT};
class B:public A{ Q_OBJECT};
class C:public QObject{Q_OBJECT};
class D:public C{};
#endif // M_H

源文件:

#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
int main(){  A ma; B mb; C mc; D md;
const QMetaObject *pa=ma. metaObject ();
const QMetaObject *pb=mb. metaObject ();
cout<<pa->className()<<endl; //输出类名 A
//使用 QMetaObject::inherits()函数判断继承关系。
cout<<pa->inherits(pa)<<endl; //输出 1,类被认为是自身的子类
cout<<pa->inherits(pb)<<endl; //输出 0,由 pb 所描述的类 B 不是类 A 的子类。
cout<<pb->inherits(pa)<<endl; //输出 1,由 pa 所描述的类 A 是类 B 的子类。
//使用 QObject::inherits()函数判断继承关系。
cout<<ma.inherits("B")<<endl; //输出 0,类 A 不是类 B 的子类。
cout<<ma.inherits("A")<<endl; //输出 1,类被认为是自身的子类
cout<<md.inherits("D")<<endl; //输出 0,因为类 D 未启动元对象系统。
cout<<md.inherits("C")<<endl; /*输出 1,虽然类 D 未启动元对象系统,但类 C 已启动,此种情形下
能正确判断继承关系。*/
cout<<md. metaObject ()->className()<<endl; /*输出 C,此处未输出 D,因为类 D 未启动元对象系统,
与类 D 最接近的启动了元对象系统的父类是 C,因此返回 C。*/
return 0; }
  1. qobject_cast函数,使用语法如下:
DestType* qobject_cast<DestType*>(QObject* p);

1、该函数类似于 C++中的 dynamic_cast,但执行速度比 dynamic_cast 更快,且不需要
C++的 RTTI 的支持,但 qobject_cast 仅适用于 QObject 及其派生类。
2、主要作用是把源类型 QObject 转换为尖括号中的目标类型 DesType(或者子类型),并
返回指向目标类型的指针,若转换失败,则返回 0。或者说源类型 QObject 属于目标
类型 DestType(或其子类型),则返回指向目标类型的指针,否则返回 0。
3、使用 qobject_cast 的条件:目标类型 DestType 必须继承(直接或间接)自 QObject,并
使用 Q_OBJECT 宏。

一个示例:

头文件:

#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
#include <iostream>
using namespace std;
class A:public QObject{ Q_OBJECT
public:void fa(){cout<<"FA"<<endl;}  };
class B:public A{ Q_OBJECT
public:void fb(){cout<<"FB"<<endl;}  };
class C:public QObject{Q_OBJECT
public:void fc(){cout<<"FC"<<endl;}  };
class D:public C{ public: void fd(){cout<<"FD"<<endl;} };
#endif // M_H

源文件:

#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
//qobject_cast 的简单应用(类型判断)
void g(QObject *p){
if(qobject_cast<A*>(p)) //若 p 是类 A 及其派生类类型
{cout<<"GA"<<endl;}
if(qobject_cast<B*>(p))//若 p 是类 B 及其派生类类型
{cout<<"GB"<<endl;}
else //若 p 不是类 B 及其派生类类型
cout<<"XX"<<endl;  }
int main(){  A *pa=new A; B *pb=new B; C *pc=new C; D *pd=new D;
qobject_cast<B*>(pa)->fb(); //输出 FB,转换成功后可调用子类中的函数。
//qobject_cast<D*>(pc); //错误,因为类 D 未使用 Q_OBJECT 宏。
g(pa); //输出 GA、XX。因为 pa 不是 B 及其派生类类型所以会输出 XX。
g(pb); //输出 GA、GB。因为 pb 是 A 的派生类类型,所以首先输出 GA,然后输出 GB。
g(pc); //输出 XX,因为 pc 即不是 A 也不是 B 及其派生类的类型,所以输出 XX。
g(pd); //输出 XX,原因同上。
return 0; }

未完待续!!!

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

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

相关文章

【UE4 塔防游戏系列】07-子弹对敌人造成伤害

目录 效果 步骤 一、让子弹拥有不同伤害 二、敌人拥有不同血量 三、修改“BP_TowerBase”逻辑 四、发射的子弹对敌人造成伤害 效果 步骤 一、让子弹拥有不同伤害 为了让每一种子弹拥有不同的伤害值&#xff0c;打开“TotalBulletsCategory”&#xff08;所有子弹的父类…

架构训练营:3-3设计备选方案与架构细化

3架构中期 什么是备选架构&#xff1f; 备选架构定义了系统可行的架构模式和技术选型 备选方案筛选过程 头脑风暴 &#xff1a;对可选技术进行排列组合&#xff0c;得到可能的方案 红线筛选&#xff1a;根据系统明确的约束和限定&#xff0c;一票否决某些方案&#xff08;主要…

为 GitHub 设置 SSH 密钥

1. 起因 给自己的 github 改个名&#xff0c;顺便就给原来 Hexo 对应的仓库也改了个名。然后发现 ubhexo clean && hexo generate && hexo deploy 失败了&#xff0c;报错如下&#xff1a; INFO Deploying: git INFO Clearing .deploy_git folder... INFO …

Hive自定义函数

本文章主要分享单行函数UDF&#xff08;一进一出&#xff09; 现在前面大体总结&#xff0c;后边文章详细介绍 自定义函数分为临时函数与永久函数 需要创建Java项目&#xff0c;导入hive依赖 创建类继承 GenericUDF&#xff08;自定义函数的抽象类&#xff09;&#xff08;实现…

仓库管理软件有哪些功能?2023仓库管理软件该如何选?

对于现代企业或批发零售商&#xff0c;高效的仓库管理是确保供应链运作顺畅、库存控制精准的关键要素。在数字化时代&#xff0c;越来越多的企业和商户意识到采用仓库管理软件的重要性。 无论您是中小型企业还是中小商户&#xff0c;仓库管理都是不可忽视的一环。 一、选择仓库…

边缘计算在智慧校园应用,实现校园智能化管理

随着科技的发展和互联网技术进步&#xff0c;校园管理正逐步实现数字化、智能化转型。边缘计算作为一种新兴技术&#xff0c;通过在离数据源较近的地方进行数据处理&#xff0c;实现了实时性分析与响应&#xff0c;为校园带来了更智能、安全的管理方式。 学生学习状态监控 AI动…

AI Chat 设计模式:8. 门面(外观)模式

本文是该系列的第八篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 请介绍一下门面模式A.1Q.2 该模式由哪些角色组成呢A.2Q.3 举一个门面模式的例子A.3Q.4…

串口wifi6+蓝牙二合一系列模块选型参考和外围电路参考设计-WG236/WG237

针对物联网数据传输&#xff0c;智能控制等应用场景研发推出的高集成小尺寸串口WiFi串口蓝牙的二合一组合模块。WiFi符合802.11a/b/g/n无线标准&#xff0c;蓝牙支持低功耗蓝牙V4.2/V5.0 BLE/V2.1和EDR&#xff0c;WiFi部分的接口是UART&#xff0c;蓝牙部分是UART/PCM 接口。模…

深入解析浏览器Cookie(图文码教学)

深入解析浏览器Cookie 前言一、什么是 Cookie?二、Cookie的特点二、如何创建 Cookie&#xff1f;三、服务器如何获取 Cookie四、Cookie 值的修改4.1 方案一4.2 方案二 五、浏览器查看 Cookie六、Cookie 生命控制七、Cookie 有效路径 Path 的设置八、案例&#xff1a;Cookie 练…

经典常谈思维导图怎么制作?手把手教你制作

经典常谈思维导图怎么制作&#xff1f;创建思维导图可以帮助我们更好地组织和整理信息&#xff0c;帮助我们更好地理解和记忆信息。它可以使我们更高效地学习和工作&#xff0c;并帮助我们更好地表达和分享我们的想法和想法。因此&#xff0c;制作思维导图是一种非常有用的技能…

回首2023上半年:成长、思考、感恩

文章目录 每日一句正能量前言一、目标达成情况总结二、工作和学习成果总结三、下半年规划总结四、个人想法 后记附录 每日一句正能量 做一个向日葵族&#xff0c;面对阳光&#xff0c;不自艾自怜&#xff0c;每天活出最灿烂的自己。曾经拥有的&#xff0c;不要忘记。不能得到的…

2023-7-12-第十七式状态模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

为什么单片机可以直接烧录程序的原因是什么?

单片机&#xff08;Microcontroller&#xff09;可以直接烧录程序的原因主要有以下几点&#xff1a; 集成性&#xff1a;单片机是一种高度集成的芯片&#xff0c;内部包含了处理器核心&#xff08;CPU&#xff09;、存储器&#xff08;如闪存、EEPROM、RAM等&#xff09;、输入…

Linux: USB Gadget 驱动简介

文章目录 1. 前言2. 背景3. USB Gadget 驱动3.1 什么是 USB Gadget 驱动&#xff1f;3.2 USB Gadget 驱动框架3.3 USB 设备控制器(UDC) 驱动3.3.1 USB 设备控制器(UDC) 驱动 概述3.3.2 USB 设备控制器(UDC) 驱动示例 3.4 USB Gadget Function 驱动3.5 USB Gadget 驱动3.5.1 USB…

微服务系列文章之 nginx负载均衡

nginx负载均衡 负载均衡建立在现有网络结构之上&#xff0c;提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽&#xff0c;增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 随着网站的发展&#xff0c;服务器压力越来越大&#xff0c;我们可能首先会将数…

CentOS 安装字体 微软雅黑

fc-list命令查看已经安装的字体 fc-list :langzh命令可以查看已安装的中文字体 找到windows系统里面的字体 上传到服务器 /usr/share/fonts/winFonts 下&#xff0c;winFonts目录是自己建立的&#xff0c;名称无要求 如果C:\Windows\Fonts下的字体没法直接传输将这个文件夹复…

东方通信基于 KubeSphere 的云计算落地经验

作者&#xff1a;周峰 吴昌泰 公司简介 东方通信股份有限公司&#xff08;以下简称“东方通信”&#xff09;创立于 1958 年&#xff0c;是一家集硬件设备、软件、服务为一体的整体解决方案提供商。公司于 1996 年成功改制上市&#xff0c;成为上海证交所同时发行 A 股和 B 股…

jenkins手把手教你从入门到放弃01-jenkins简介(详解)

一、简介 jenkins是一个可扩展的持续集成引擎。持续集成&#xff0c;也就是通常所说的CI&#xff08;Continues Integration&#xff09;&#xff0c;可以说是现代软件技术开发的基础。持续集成是一种软件开发实践&#xff0c; 即团队开发成员经常集成他们的工作&#xff0c;通…

Ribbon 负载均衡服务调用

文章目录 1 SpringCloud Load Balance2 总结:3 Ribbon工作流程&#xff1a;4 自定义Ribbon 负载均衡算法&#xff1a;4.1 iRule接口&#xff1a;4.2 Ribbon自带的负载均衡算法&#xff1a;4.3 负载均衡算法替代&#xff1a;4.3.1、在非启动类包及子包下创建配置类4.3.2、定义4.…

网络虚拟化相关的Linux接口介绍

Linux拥有丰富的网络虚拟化功能&#xff0c;能被虚拟机&#xff0c;容器还有云网络使用。在这篇文章中&#xff0c;我会给出所有通用网络虚拟化接口的简要介绍。没有代码分析&#xff0c;只有简短的接口介绍和在Linux上的使用操作。这系列接口都可以使用ip link命令实现。 这篇…