Qt元对象系统 day5

Qt元对象系统 day5

内存管理

  • QObject以对象树的形式组织起来,当为一个对象创建子对象时,子对象回自动添加到父对象的children()列表中。父对象拥有子对象所有权,比如父对象可以在自己的析构函数中删除它的孩子对象。使用findChild()或findChildren()通过名字和类型查询孩子对象
QObject(QObject *parent = nullptr)
  • QObject及其派生类的对象,如果其parent非nullptr,那么其parent析构时会析构该对象。

  • 父子关系:父对象、子对象、父子关系。这是Qt中所特有的,与类的继承关系无关,传递参数是与parent有关(基类、派生类,或父类、子类,这是对于派生体系来说的,与parent无关)。

  • 在Qt中,最基础和核心的类是:QObject,QObject内部有一个名为children的QObjectList列表,会保存所有子对象,还有一个指针parent,用来指向父对象,当自己析构时,会先把自己从parent列表中删除并且析构所有的children。

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QRadioButton>//只有在debug模式下才显示调试窗口,如果在release模式下不显示调试窗口
#ifdef _DEBUG#pragma comment(linker,"/subsystem:console /entry:mainCRTStartup")
#else#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
#endif // _DEBUGint main(int argc, char* argv[])
{QApplication a(argc, argv);QWidget w;{QRadioButton* rBtn = new QRadioButton("男", &w);//设置对象名rBtn->setObjectName("man_rBtn");auto btn = new QPushButton("小瓜");//设置对象名btn->setObjectName("小瓜_大瓜");//如果指定了父对象,则不需要手动showbtn->setParent(&w);rBtn->move(100, 0);QObject::connect(btn, &QPushButton::clicked, [](){qDebug() << "小瓜";});//获取btn的父对象auto parentw = dynamic_cast<QWidget*>(btn->parent());//是否获取成功if (parentw){qDebug() << parentw;}//获取子对象列表const QObjectList& list = w.children();for (auto v : list){qDebug() << v;}//查找指定类型的子对象qDebug() << "sub object" << w.findChild<QPushButton*>();//查找指定的子对象名的子对象qDebug() << w.findChild<QWidget*>("小瓜_大瓜");}//把所有子对象添加到窗口之后再显示窗口w.show();return a.exec();
}
  • 运行结果
    在这里插入图片描述

释放内存

  • Qt里面有些还是得手动释放
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QRadioButton>//只有在debug模式下才显示调试窗口,如果在release模式下不显示调试窗口
#ifdef _DEBUG#pragma comment(linker,"/subsystem:console /entry:mainCRTStartup")
#else#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
#endif // _DEBUGint main(int argc, char* argv[])
{QApplication a(argc, argv);auto w = new QWidget;w->show();QObject::connect(w, &QObject::destroyed, []() {qDebug() << "释放成功";});int ret = a.exec();//1.直接使用delete释放(对于直接或间接继承QObject的类对象)delete w;//2.使用QOBject提供的安全释放的函数来释放对象(下一次运行到事情循环的时候,才去释放对象)//w->deleteLater(); //此处场景不能使用,因为事件循环已经结束了,当某个窗口不需要的时候就释放掉用这个return ret;
}
  • 运行结果
    在这里插入图片描述

Qt中的智能指针

  • 为了管理内存等资源,C++程序员通常采用RAII(Resource Acquisition Is Initialization)机制:在类的构造函数中申请资源,然后使用,最后在析构函数中释放资源。
  • 如果没有智能指针,程序员必须保证new对象能在正确的时机delete,四处编写异常捕获代码以释放资源,而智能指针则可以在退出作用域时(不管是正常流程离开或是因异常离开)总调用delete来析构在堆上动态分配的对象。
  • Qt中的智能指针:
智能指针描述
QPointer[QObject专享指针]QObject或子类对象释放时会自动指向nullptr
QScopedPointer[独享指针]超出作用域自动释放管理的对象
QSharedPointer[共享指针]
QWeakPointer[监视指针]
QScopedArrayPointer[独享数组指针]超出作用域自动释放管理的对象数组
QSharedDataPointer[隐式共享指针]读时共享,写时拷贝
QExplicitlySharedDataPointer[显示共享指针]读时共享,写时需要手动拷贝(通过detach()函数)

QPointer

  • 受保护指针QPointer的行为类似于普通c++指针T *,只是当被引用的对象被销毁时它会自动清除(不像普通c++指针,在这种情况下它会变成“悬浮指针”)。T必须是QObject的子类。
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QRadioButton>
#include <QPointer>
//只有在debug模式下才显示调试窗口,如果在release模式下不显示调试窗口
#ifdef _DEBUG#pragma comment(linker,"/subsystem:console /entry:mainCRTStartup")
#else#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
#endif // _DEBUGclass Text :public QWidget
{Q_OBJECT
public:Text(QWidget* parent = nullptr) :QWidget(parent){text_QPointer();}void text_QPointer(){//QPointer不会自动释放保存的对象QPointer btn = new QPushButton("小瓜", this);if (btn){qDebug() << "小瓜";}delete btn;//释放之后,btn会自动变为nullptrif (!btn){qDebug() << "小瓜不见了";}}
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Text w;w.show();return a.exec();}#include "main.moc"
  • 运行结果
    在这里插入图片描述

QScopedPointer

  • 手动管理堆分配对象非常困难而且容易出错,通常的结果是代码泄漏内存并且难以维护。QScopedPointer是一个小型实用程序类,它通过将基于堆栈的内存所有权分配给堆分配(更通常称为资源获取初始化(RAII)),极大地简化了这一点。

  • QScopedPointer保证当当前作用域消失时,所指向的对象将被删除。

QSharedPointer

  • QSharedPointer是c++中的一个自动共享指针。它的行为和普通指针完全一样。

  • 如果没有其他QSharedPointer对象引用它,当它超出作用域时,QSharedPointer将删除它所持有的指针。

  • QSharedPointer对象可以从一个普通指针、另一个QSharedPointer对象或通过将QWeakPointer对象提升为强引用来创建。

QWeakPointer

  • 在c++中,QWeakPointer是对指针的自动弱引用。它不能用于直接解引用该指针,但可以用于验证该指针是否已在另一个上下文中被删除。

  • QWeakPointer对象只能通过从QSharedPointer赋值来创建。

QScopedArrayPointer

  • QScopedArrayPointer是一个QScopedPointer,默认使用delete[]操作符删除它所指向的对象。为了方便,它还提供了操作符[]

QSharedDataPointer

  • QSharedDataPointer类表示指向隐式共享对象的指针。

  • QSharedDataPointer使您可以轻松地编写自己的隐式共享类。QSharedDataPointer实现了线程安全的引用计数,确保将QSharedDataPointer添加到可重入类中不会使它们不可重入。

  • 许多Qt类都使用隐式共享,以将指针的速度和内存效率与类的易用性结合起来。有关更多信息,请参见共享类页面。

  • 假设您想让Employee类隐式共享。这个过程是:定义Employee类,使其拥有类型为QSharedDataPointer的单个数据成员。

  • 定义从QSharedData派生的EmployeeData类,以包含通常放入Employee类中的所有数据成员。

QExplicitlySharedDataPointer

  • QExplicitlySharedDataPointer类表示指向显式共享对象的指针。

  • QExplicitlySharedDataPointer使您可以轻松编写自己的显式共享类。QExplicitlySharedDataPointer实现了线程安全的引用计数,确保将QExplicitlySharedDataPointer添加到可重入类中不会使它们不可重入。

  • 除了一个很大的区别,QExplicitlySharedDataPointer就像QSharedDataPointer。最大的区别是,QExplicitlySharedDataPointer的成员函数在允许修改共享数据对象之前,不像QSharedDataPointer的非const成员那样在写操作(detach())时自动复制。有一个detach()函数可用,但如果您真的想要detach(),则必须自己调用它。这意味着QExplicitlySharedDataPointers的行为与常规的c++指针类似,只是通过进行引用计数并且在引用计数为0之前不删除共享数据对象,避免了悬浮指针的问题。

属性系统

  • Qt提供了一个复杂的属性系统,类似于一些编译器供应商提供的属性系统。然而,作为一个独立于编译器和平台的库,Qt不依赖于像__property或[property]这样的非标准编译器特性。Qt解决方案可以在Qt支持的每一个平台上使用任何标准的c++编译器。它基于元对象系统,也通过信号和插槽提供对象间通信。

  • 属性的行为类似于类数据成员,但它具有通过元对象系统访问的附加特性。

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QRadioButton>
#include <QPointer>
//只有在debug模式下才显示调试窗口,如果在release模式下不显示调试窗口
#ifdef _DEBUG#pragma comment(linker,"/subsystem:console /entry:mainCRTStartup")
#else#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
#endif // _DEBUGclass Person :public QObject
{Q_OBJECT
public:Person(){}
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Person xiaogua;//1.通过方法设置一些成员变量xiaogua.setObjectName("小瓜");qDebug() << xiaogua.objectName();//2.通过属性来设置xiaogua.setProperty("objectName", "大瓜");qDebug() << xiaogua.objectName() << xiaogua.property("objectName");//3.如果设置类里面没有的属性,那么则会添加临时的属性xiaogua.setProperty("name", "小瓜");xiaogua.setProperty("age", 21);qDebug() << xiaogua.property("name") << xiaogua.property("age");return a.exec();}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

声明自己的属性

  • 除了通过setProperty动态添加属性之外,怎样才能在代码中,声明属性呢?

  • 要声明属性,请在继承 QObject 的类中使用 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])
  • 如果未指定 MEMBER 变量,则需要 READ 访问器函数。 它用于读取属性值。 理想情况下,const 函数用于此目的,它必须返回属性的类型或对该类型的 const 引用。 例如,QWidget::focus 是一个带有 READ 函数的只读属性,QWidget::hasFocus()。

  • WRITE 访问器函数是可选的。 它用于设置属性值。 它必须返回 void 并且必须只接受一个参数,该参数可以是属性的类型,也可以是指向该类型的指针或引用。 例如,QWidget::enabled 具有 WRITE 函数 QWidget::setEnabled()。 只读属性不需要 WRITE 函数。 例如,QWidget::focus 没有 WRITE 功能。

  • 如果未指定 READ 访问器函数,则需要 MEMBER 变量关联。 这使得给定的成员变量可读可写,而无需创建 READ 和 WRITE 访问器函数。 如果您需要控制变量访问,除了 MEMBER 变量关联之外,仍然可以使用 READ 或 WRITE 访问器函数。

  • RESET 功能是可选的。 它用于将属性设置回其特定于上下文的默认值。 例如,QWidget::cursor 有典型的 READ 和 WRITE 函数,QWidget::cursor() 和 QWidget::setCursor(),它还有一个 RESET 函数,QWidget::unsetCursor(),因为没有调用 QWidget:: setCursor() 可以表示重置为特定于上下文的光标。 RESET 函数必须返回 void 并且不带任何参数。

  • NOTIFY 信号是可选的。 如果已定义,则应指定该类中的一个现有信号,该信号在属性值更改时发出。 MEMBER 变量的 NOTIFY 信号必须采用零个或一个参数,该参数必须与属性的类型相同。 该参数将采用属性的新值。 NOTIFY 信号只应在属性真正被更改时发出,以避免在 QML 中不必要地重新评估绑定,例如。 当没有显式设置器的 MEMBER 属性需要时,Qt 会自动发出该信号。

  • REVISION 编号或 REVISION() 宏是可选的。 如果包含,它定义了要在 API 的特定修订版中使用的属性及其通知信号(通常用于暴露于 QML)。 如果不包含,则默认为 0。

  • DESIGNABLE 属性指示该属性是否应在 GUI 设计工具(例如 Qt Designer)的属性编辑器中可见。 大多数属性都是可设计的(默认为 true)。 有效值为真和假。

  • SCRIPTABLE 属性指示脚本引擎是否可以访问此属性(默认为 true)。 有效值为真和假。

  • STORED 属性指示该属性是否应该被认为是独立存在的,或者取决于其他值。 它还指示在存储对象的状态时是否必须保存属性值。 大多数属性是 STORED 的(默认为 true),但例如,QWidget::minimumWidth() 的 STORED 为 false,因为它的值只是从属性 QWidget::minimumSize() 的宽度分量中获取的,它是一个 QSize。

  • USER 属性指示该属性是被指定为该类的面向用户的属性还是用户可编辑的属性。 通常,每个类只有一个 USER 属性(默认为 false)。 例如,QAbstractButton::checked 是(可检查)按钮的用户可编辑属性。 请注意,QItemDelegate 获取和设置小部件的 USER 属性。

  • BINDABLE bindableProperty 属性表明该属性支持绑定,并且可以通过元对象系统 (QMetaProperty) 设置和检查与该属性的绑定。 bindableProperty 命名 QBindable 类型的类成员,其中 T 是属性类型。 这个属性是在 Qt 6.0 中引入的。

  • CONSTANT 属性的存在表明属性值是常量。 对于给定的对象实例,常量属性的 READ 方法在每次调用时都必须返回相同的值。 对于对象的不同实例,该常数值可能不同。 常量属性不能有 WRITE 方法或 NOTIFY 信号。

  • FINAL 属性的存在表明该属性不会被派生类覆盖。 这在某些情况下可用于性能优化,但并非由 moc 强制执行。 必须注意永远不要覆盖 FINAL 属性。

  • REQUIRED 属性的存在表明该属性应该由该类的用户设置。 这不是由 moc 强制执行的,并且对于暴露给 QML 的类最有用。 在 QML 中,除非设置了所有 REQUIRED 属性,否则无法实例化具有 REQUIRED 属性的类。

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QRadioButton>
#include <QPointer>
//只有在debug模式下才显示调试窗口,如果在release模式下不显示调试窗口
#ifdef _DEBUG#pragma comment(linker,"/subsystem:console /entry:mainCRTStartup")
#else#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
#endif // _DEBUG
int g_tel=666;
class Person :public QObject
{Q_OBJECT//让成员变量暴露给属性Q_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged)Q_PROPERTY(int age MEMBER m_age NOTIFY ageChanged)//直接定义属性Q_PROPERTY(quint64 tel READ getTel WRITE setTel RESET resetTel NOTIFY telChanged)public:Person(){}//提供接口int age()const{ return m_age;}void setAge(int age) { m_age = age; }QString name() const { return m_name; }void setName(const QString& name) { m_name = name; }quint64 getTel()const { return g_tel; }void setTel(quint64 tel){if (g_tel != tel){g_tel = tel;emit telChanged(tel);}}void resetTel() { setTel(-1); }signals:void telChanged(quint64 tel);void ageChanged(int age);void nameChanged();protected:int m_age{};QString m_name{};
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Person xiaogua;xiaogua.setName("小瓜");xiaogua.setAge(20);QObject::connect(&xiaogua, &Person::telChanged, [](quint64 tel){qDebug() << "tel changed" << tel;});QObject::connect(&xiaogua, &Person::ageChanged, [](){qDebug() << "age changed";});QObject::connect(&xiaogua, &Person::nameChanged, [](){qDebug() << "name changed";});//xiaogua.setTel(12345678);xiaogua.setProperty("tel", QVariant());xiaogua.setAge(21);xiaogua.setProperty("name", "ccc");qDebug() << xiaogua.property("name") << xiaogua.property("age") << xiaogua.property("tel");return a.exec();}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

绑定属性

  • Qt提供了可绑定属性。可绑定属性是具有值或使用任何c++函数(通常是c++ lambda表达式)指定的属性。如果它们是使用c++函数指定的,那么当它们的依赖项发生变化时,它们就会自动更新。
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QRadioButton>
#include <QPointer>
#include<QProperty>
#include<QObjectBindableProperty>
//只有在debug模式下才显示调试窗口,如果在release模式下不显示调试窗口
#ifdef _DEBUG#pragma comment(linker,"/subsystem:console /entry:mainCRTStartup")
#else#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
#endif // _DEBUG//1,在Qobject子类中使用绑定属性
struct Circle : public QObject
{Q_OBJECT
public:Circle(){//添加绑定area.setBinding([=]{return M_PI * radius * radius;});}//QProperty<int> radius{};//QProperty<qreal> area{};signals:void radiusChanged();void areaCahnged();
public:Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Circle, int, radius, 0, &Circle::radiusChanged);Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Circle, qreal, area, 0, &Circle::areaCahnged);
};
//2,如果类没有继承自Qobject
struct Rect
{Rect(){area.setBinding([=]{return w * h;});}QProperty<int> w;QProperty<int> h;QProperty<int> area;
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Circle c;QObject::connect(&c, &Circle::areaCahnged, []{qDebug() << "areaChange";});c.radius = 23;qDebug() << c.area;Rect r;r.w = 5;r.h = 9;qDebug() << r.area;return a.exec();
}#include "main.moc"

内省机制

  • 所谓内省是指面向对象语言的一种在运行期间查询对象信息的能力, 比如如果该语具有运行期间检查对象型别的能力,那么我们称它是型别内省(type intropection)的,型别内省可以用来实施多态。
  • Qt拓展了C++的内省机制,(实际上,它并没有采用C++的RTTI),而是提供了更为强大的元对象(meta object)机制,来实现内省。接下来,就让我们看看,Qt是如何扩展c++内省机制的。
  • 要深刻理解Qt的内省机制,首先理解QObjectQObject类是整个Qt对象模型的心脏,Qt对象模型最为核心的功能是提供一种无缝的对象通讯机制,即就是我们所熟知的信号和槽。QObject主要有三大职责: 内存管理、内省(intropection)与事件处理。本文将集中在在内省的讨论。以下代码介绍了QObject类提供的内省方法:
//判断该类是否继承自指定的类
bool inherits(const char *className) const;QWidget* w = new QWidget;
w.inherits("QObject");		//true
w.inherits("QWidget");		//false
  • 示例
#include<QApplication>
#include<QWidget>
#include<QMetaEnum>
class Test
{
public:Test(){QWidget w;qDebug() << w.inherits("QObject");auto metaObject =  w.metaObject();qDebug()<<metaObject->className();}
};//1,在命名空间中使用注册枚举
namespace Maye
{Q_NAMESPACEenum Type{Player,Enemy,Bullet};Q_ENUM_NS(Type)	//把枚举类型注册到元对象系统
}
//2,在类中注册枚举
struct Person : public QObject 
{Q_OBJECT
public:enum Identity{Student,Doctor,Teacher};Q_ENUM(Identity)
};
int main(int argc, char* argv[])
{QApplication a(argc, argv);Test t;using namespace Maye;Type type = Player;qDebug() << type;	//0  PlayerPerson::Identity id = Person::Doctor;qDebug() << id;//获取枚举信息QMetaEnum me = QMetaEnum::fromType<Person::Identity>();qDebug() << me.name() << me.keyCount();qDebug() << me.keyToValue("Teacher");qDebug() << me.valueToKey(Person::Doctor);return a.exec();
}
#include "main.moc"

QFlags

#include<QApplication>
#include<QFlags>
struct xiaogua : public QObject
{Q_OBJECT
public:enum AlignMent{Top = 1,Left = 1 << 2,Bottom = 1 << 3,Right = 1 << 4,Center = 1 << 5};Q_ENUMS(AlignMent)Q_DECLARE_FLAGS(AlignMentFlags, AlignMent)Q_FLAG(AlignMentFlags)
};int main(int argc, char* argv[])
{QApplication a(argc, argv);xiaogua::AlignMentFlags flags(xiaogua::Top | xiaogua::Left);if (flags.testFlag(xiaogua::Top)){qDebug() << "has top";}if (flags.testFlag(xiaogua::Left)){qDebug() << "has left";}return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

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

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

相关文章

VR模拟鸡胚培养接种实验,打造沉浸式的学习环境

在医学教育领域&#xff0c;传统的鸡胚接种实验一直是教学的重要组成部分。然而&#xff0c;这种实验方法存在一定的局限性&#xff0c;如操作难度大、成本高、安全隐患等。为了解决这些问题&#xff0c;越来越多的教育机构开始尝试引入虚拟现实(VR)技术&#xff0c;以模拟鸡胚…

【threejs】基本编程概念及海岛模型展示逻辑

采用three封装模式完成的海岛动画&#xff08;点击这里查看&#xff09; 直接上代码吧 <template><div class"scene"><video id"videoContainer" style"position:absolute;top:0px;left:0px;z-index:100;visibility: hidden"&g…

JavaEE-文件IO操作

构造方法 一般方法&#xff0c;有很多&#xff0c;我们以下只是列举几个经常使用的 注意在上述的操作过程中&#xff0c;无论是绝对路径下的这个文件还是相对路径下的这个文件&#xff0c;都是不存在的 Reader 使用 --> 文本文件 FileReader类所涉及到的一些方法 Fil…

RabbitMQ集群搭建详细介绍以及解决搭建过程中的各种问题 + 配置镜像队列——实操型

RabbitMQ集群搭建详细介绍以及解决搭建过程中的各种问题 配置镜像队列——实操型 1. 准备工作1.1 安装RabbitMQ1.2 简单部署搭建设计1.3 参考官网 2. RabbitMQ 形成集群的方法3. 搭建RabbitMQ集群3.1 部署架构3.2 rabbitmq集群基础知识3.2.1 关于节点名称&#xff08;标识符&a…

ORB-SLAM2运行自己的数据集进行定位教程

ORB-SLAM2只做定位的话&#xff0c;精度还是挺准确的&#xff0c;所以用单目摄像头录制视频&#xff0c;制作自己的数据集跑一下&#xff0c;看看定位精度&#xff0c;将过程加以记录。 文章目录 一、系统配置二、制作数据集1、脚本编写2、配置文件编写3、录制视频素材&#x…

基于ModbusTCP与西门子PLC通讯项目案例

目录 一、西门子PLC仿真环境搭建 【1.1】创建PLC项目 【1.2】编写PLC程序 二、C#代码编写 【2.1】窗口制作 【2.2】效果演示 【2.3】读取源码 【2.4】FrmSiemensSet源码 【2.5】Variable源码 一、西门子PLC仿真环境搭建 【1.1】创建PLC项目 搭建PLCSIM-Advacend模拟仿…

三相PWM整流器滞环电流控制Simulink仿真模型

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Maven 配置阿里云镜像

1. 查找maven setting.xml配置文件 find / -name "setting.xml" 2. 添加阿里云镜像 修改maven根目录下的conf文件夹中的setting.xml文件中的mirrors下添加mirror标签 <settings> <localRepository>E:\Maven\repository</localRepository> <…

【哈士奇赠书活动 - 41期】- 〖产品设计软技能:创业公司篇〗

文章目录 ⭐️ 赠书 - 《产品设计软技能&#xff1a;创业公司篇》⭐️ 内容简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 赠书活动 → 获奖名单 ⭐️ 赠书 - 《产品设计软技能&#xff1a;创业公司篇》 ⭐️ 内容简介 在创业公司设计产品与在成熟公司设计产品存在明显差异。《产品设计软…

混沌工程初分享

混沌工程初分享 一、什么是混沌工程 1、什么是混沌 混沌是一种现象&#xff0c;在一个动力系统中&#xff0c;因为各种不同的参数变化导致的一系列的连锁反应。比如&#xff1a; 在南美洲亚马逊河流域热带雨林中的蝴蝶&#xff0c;偶尔的几次振翅&#xff0c;可以在两周以后引…

全栈开发笔记2:项目部署上线的三种方式

文章目录 最原始的方式宝塔Docker 部署其他 本文为编程导航实战项目学习笔记。 项目部署的三种方式&#xff1a; 最原始方式✅ yum 手动安装 jdk mysql tomcat nginx打包前端项目&#xff0c;放到某个目录&#xff0c;修改 nginx 配置修改线上的 mysql 配置&#xff0c;打包 j…

国庆出游远程实测:ToDesk 、TeamViewer、AnyDesk远程控制软件稳定性

ToDesk 、TeamViewer、AnyDesk远程控制软件稳定性 【前言】【实测软件】【测试环境】【实操体验】1. 软件安装2. 登录速度3. 文件传输4. 操作延迟5. 画面清晰度6. 安全防护 【本文小结】 【前言】 随着科技的不断发展&#xff0c;远程控制软件已成为我们生活中不可或缺的一部分…