qt中的对象树与生命期

news2025/1/10 12:08:31

1.为什么要使用对象树: GUI 程序通常是存在父子关系的,比如一个对话框之中含有按钮、列表等部件,按钮、列表、对话框等部件其实就是一个类的对象(注意是类的对象,而非类),很明显这些对象之间是存在父子关系的,因此一个 GUI 程序通常会由一个父对象维护着一系列的子对象列表,这样更方便对部件的管理,比如当按下 tab 键时,父对象会依据子对象列表令各子对象依次获得焦点。当关闭对话框时,父象依据子对象列表,找到每个子对象,然后删除它们。 在 Qt 中,对对象的管理,使用的是树形结构,也就是对象树

组合模式与对象树

1、组合模式指的是把类的对象组织成树形结构,这种树形结构也称为对象树, Qt 使用对象树来管理 QObject 及其子类的对象。 注意:这里是指的类的对象而不是类。把类组织成树形结构只需使用简单的继承机制便可实现。
2、使用组合模式的主要作用是可以通过根节点对象间接调用子节点中的虚函数,从而可以间接的对子节点对象进行操作。
3、组合模式的基本思想是使用父类类型的指针指向子类对象,并把这个指针存储在一个数组中(使用容器更方便),然后每创建一个类对象就向这个容器中添加一个指向该对象的指针。

示例:打印出组合模式中的各对象的名称

在这里插入图片描述

#include "mainwindow.h"
#include <QApplication>
#include<iostream>
#include<QVector>
#include<QDebug>
using namespace std;
class A{
public:
    //也可使用A* m[11] 这种方式的数组代替,但数组
    //无法自动扩容
    QVector<A*> v;
    void add(A* ma)
    {
        v.push_back(ma);
    }
    QString name;
    A(){}
    A(QString n){name = n;}
    virtual QString g() {return name;}
    void f(){
        if(!v.isEmpty())
        {
            qDebug()<<name<<"==";
            for(int i= 0;i<v.size();i++)
            {
                qDebug()<<v[i]->g()<<",";
            }
            qDebug()<<"------------this is A-------------";
        }
    }
    //该函数会被递归调用,用以输出整个对象树中对象的名称
    virtual void pt()
    {
        qDebug()<<"--------this is-A--~--:"<<name;
        f();
        pt是虚函数,若v[i]的类型为子类型B。会调用B::pt()
        for(int i= 0;i<v.size();i++)
        {
            v[i]->pt();
        }
    }
};
class B: public A{     //需要继承A
public:
    QString name;
    B(QString str) {name = str;}

    void add(A *ma)
    {
        v.push_back(ma);
    }
    virtual QString g() {return name;}
    void f(){
        if(!v.isEmpty())
        {
            qDebug()<<name<<"==";
            for(int i= 0;i<v.size();i++)
            {
                qDebug()<<v[i]->g()<<",";
            }
            qDebug()<<"------------this is B-------------";
        }
    }
    void pt()
    {
        qDebug()<<"--------this is---B~--:"<<name;
        f();
        pt是虚函数,若v[i]的类型为子类型B。会调用B::pt()
        for(int i= 0;i<v.size();i++)
        {
            v[i]->pt();
        }
    }
};
//需要继承自类A,该类无add函数,也就是说该类的对象不能有子对象。
class C: public A{
public:
    QString name;
    C(QString str) {name = str;}
    QString g() {return name;}
    void f(){
        if(!v.isEmpty())
        {
            qDebug()<<name<<"==";
            for(int i= 0;i<v.size();i++)
            {
                qDebug()<<v[i]->g()<<",";
            }
            qDebug()<<"---------this is C----------------";
        }
    }
    void pt()
    {
        qDebug()<<"--------this is ~~~c :"<<name;
        f();
        for(int i= 0;i<v.size();i++)
        {
            v[i]->pt();
        }
    }
};
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    A ma("ma"),ma1("ma1"),ma2("ma2"),ma3("ma3"),ma4("ma4");
    B mb("mb"),mb1("mb1"),mb2("mb2"),mb3("mb3"),mb4("mb4");
    C mc("mc"),mc1("mc1"),mc2("mc2"),mc3("mc3"),mc4("mc4");
    ma.add(&mb); //ma.v[0]=&mb;
    mb.add(&mb1); //mb.v[0]=&mb1;
    mb.add(&mb2); //mb.v[1]=&mb2;
    ma.add(&mb1); //ma.v[1]=&mb1;
    mb1.add(&mb3); //mb1.v[0]=&mb3;
    mb1.add(&mb4); //mb1.v[1]=&mb4;
    mb2.add(&mc1); //mb2.v[0]=&mc1;
    mb2.add(&mc2); //mb2.v[1]=&mc2;
    mb2.add(&ma1); //mb2.v[2]=&ma1;
    qDebug()<<QString::fromLocal8Bit("各对象拥有的子对象")<<endl;
    ma.f(); mb.f(); mb1.f(); mb2.f();
    qDebug()<<endl<<QString::fromLocal8Bit("整个对象中结构")<<endl;
    ma.pt();
    w.show();
    return a.exec();
}

输出:

"ma" ==
"mb" ,
"mb1" ,
------------this is A-------------
"mb" ==
"mb1" ,
"mb2" ,
------------this is B-------------
"mb1" ==
"mb3" ,
"mb4" ,
------------this is B-------------
"mb2" ==
"mc1" ,
"mc2" ,
"ma1" ,
------------this is B-------------

"整个对象中结构" 

--------this is-A--~--: "ma"
"ma" ==
"mb" ,
"mb1" ,
------------this is A-------------
--------this is---B~--: "mb"
"mb" ==
"mb1" ,
"mb2" ,
------------this is B-------------
--------this is---B~--: "mb1"
"mb1" ==
"mb3" ,
"mb4" ,
------------this is B-------------
--------this is---B~--: "mb3"
--------this is---B~--: "mb4"
--------this is---B~--: "mb2"
"mb2" ==
"mc1" ,
"mc2" ,
"ma1" ,
------------this is B-------------
--------this is ~~~c : "mc1"
--------this is ~~~c : "mc2"
--------this is-A--~--: "ma1"
--------this is---B~--: "mb1"
"mb1" ==
"mb3" ,
"mb4" ,
------------this is B-------------
--------this is---B~--: "mb3"
--------this is---B~--: "mb4"

使用父节点对象删除子结构对象

代码如下:

#include "mainwindow.h"
#include <QApplication>
#include<iostream>
#include<QVector>
#include<QDebug>
using namespace std;
class A{
public:
    QVector<A*> v;
    QString name;
    A(){}
    A(QString n)
    {
        qDebug()<<"~~~this is construct:A(QString n)";
        name = n;
    }
    A(A *ma,QString n)
    {
        qDebug()<<"~~~this is construct:  A(A *ma,QString n)";
        name = n;ma->v.push_back(this);
    }
    virtual QString g() {return name;}
    virtual ~A()
    {
        qDebug()<<"~A()";
        if(!v.isEmpty())
        {
            qDebug()<<"~~~~~~~~start~~~~~~~~";
            for(int i= 0;i<v.size();i++)
            {
                qDebug()<<"~~~object= "<<i<<" is:"<<v[i]->g()<<endl;
                delete v[i];
            }
            qDebug()<<"~~~~~~~~end~~~~~~~~";
        }
    }
};
class B: public A{
public:
    QString name;
    B(QString str)
    {
        qDebug()<<"~~~this is construct:B(QString n)";
        name = str;
    }
    B(A *ma,QString n):A(ma,n)
    {
        qDebug()<<"~~~this is construct: B(A *ma,QString n):A(ma,n)";
        name =n;
    }
    ~B()
    {
        qDebug()<<" ~B()~~name:"<<g();
    }
    virtual QString g() {return name;}
};

class C: public A{
public:
    QString name;
    C(A* ma, QString str):A(ma,str)
    {
        qDebug()<<"~~~this is construct: C(A* ma, QString str)";
        name = str;
    }
    QString g() {return name;}
    ~C()
    {
        qDebug()<<"~C()~~name:"<<g();
    }
};
class D: public B{
public:
    QString name;
    D(A* ma, QString str):B(ma,str)
    {
        qDebug()<<"~~~this is construct: D(A* ma, QString str)";
        name = str;
    }
    QString g() {return name;}
    ~D()
    {
        qDebug()<<"~D()~~name:"<<g();
    }
};
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    A ma("ma");
    B *pb = new B(&ma,"pb");
    B *pb1 = new B(&ma,"pb1");
    B *pb2=new B(pb,"pb2");
    C *pc1=new C(pb,"pc1");
    A* pa1=new A(pb1,"pa1");
    B* pb3=new B(pb1,"pb3");
    C* pc2=new C(pb1,"pc2");
    D* pd1=new D(pc1,"pd1");
    D* pd2=new D(pc1,"pd2");
    w.show();
    qDebug()<<"---------------------------------------------------";
    return a.exec();
}

输出说明:

//A ma("ma");
~~~this is construct:A(QString n)
// B *pb = new B(&ma,"pb");  先A的构造后B的,因为B的基类是A
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: B(A *ma,QString n):A(ma,n)
//B *pb1 = new B(&ma,"pb1");   先A的构造后B的
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: B(A *ma,QString n):A(ma,n)
//B *pb2=new B(pb,"pb2");    先A的构造后B的
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: B(A *ma,QString n):A(ma,n)
// C *pc1=new C(pb,"pc1");    先A后C
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: C(A* ma, QString str)
//A* pa1=new A(pb1,"pa1");
~~~this is construct:  A(A *ma,QString n)
//B* pb3=new B(pb1,"pb3");  先A后B , pb1已经构造好的对象,传参走B的构造
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: B(A *ma,QString n):A(ma,n)
//C* pc2=new C(pb1,"pc2");    先A后C
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: C(A* ma, QString str)
// D* pd1=new D(pc1,"pd1");  D继承B,B继承A,因此构造顺序 A B D
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: B(A *ma,QString n):A(ma,n)
~~~this is construct: D(A* ma, QString str)
// D* pd2=new D(pc1,"pd2"); D继承B,B继承A,因此构造顺序 A B D 
~~~this is construct:  A(A *ma,QString n)
~~~this is construct: B(A *ma,QString n):A(ma,n)
~~~this is construct: D(A* ma, QString str)1//函数中有指针,临时变量,因此优先析构临时变量,指针需要手动delete 否则由程序自动释放
~A()
~~~~~~~~start~~~~~~~~
//v中有pb,pb1,先析构pb
~~~object=  0  is: "pb" 

 ~B()~~name: "pb"
 //要析构pb 先析构pb的子对象,pb2和pc1
~A()
~~~~~~~~start~~~~~~~~
~~~object=  0  is: "pb2" 

 ~B()~~name: "pb2"
 //析构pc1
~A()
~~~object=  1  is: "pc1" 
//pc1又有子对象pd1,和pd2
~C()~~name: "pc1"
//析构pd1  D继承B,B继承A ,因此析构顺序 D B A
~A()
~~~~~~~~start~~~~~~~~
~~~object=  0  is: "pd1" 
//
~D()~~name: "pd1"
 ~B()~~name: "pd1"
 //析构pd2
~A()
~~~object=  1  is: "pd2" 

~D()~~name: "pd2"
 ~B()~~name: "pd2"
~A()
~~~~~~~~end~~~~~~~~ //pd2析构完成
~~~~~~~~end~~~~~~~~//pb析构完成
//开始析构pb1
~~~object=  1  is: "pb1" 
//pb1包含 pa1,pb3,pc2
 ~B()~~name: "pb1"
~A()
~~~~~~~~start~~~~~~~~
~~~object=  0  is: "pa1" 

~A()
~~~object=  1  is: "pb3" 

 ~B()~~name: "pb3"
~A()
~~~object=  2  is: "pc2" 

~C()~~name: "pc2"
~A()
~~~~~~~~end~~~~~~~~
~~~~~~~~end~~~~~~~~

QObject类、对象树、生命期

  1. QObject 类是所有 Qt 对象的基类,是 Qt 对象模型的核心,所有 Qt 部件都继承自 QObject。
  2. QObject 及其派生类的单形参构造函数应声明为 explicit,以避免发生隐式类型转换。
  3. QObject 类既没有复制构造函数也没有赋值操作符函数(实际上它们被声明为私有的),因此无法通过值传递的方式向函数传递一个 QObject 对象。
  4. Qt 库中的 QObject 对象是以树状结构组织自已的,当创建一个 QObject 对象时,可以为其设置父对象,新创建的对象会被加入到父对象的子对象列表中(可通过QObject::children()函数查询), 因为 Qt 的部件类,都是以 QObject 为基类,因此, Qt 的所有部件类都具有对象树的特性。
  5. 对象树的组织规则:
    ①、 每一个 QObject 对象只能有一个父 QObject 对象,但可以有任意数量的子 QObject 对象。 比如
    A ma; B mb; C mc;
    ma.setParent(&mb); //将对象 ma 添加到 mb 的子对象列表中,
    ma.setParent(&mc); //该语句会把 ma 从 mb 的子对象列表中移出,并将其添加到mc 的子对象列表中。 setParent 函数见后文。
    ②、 QObject 对象会把指向各个子对象地址的指针放在 QObjectList 之中。 QObjectList 是QList<QObject*>的别名, QList 是 Qt 的一个列表容器。
  6. 对象删除规则(注意:以下规则并非 C++语法规则,而是 Qt 的对象树规则):
    ①、 基本规则: 父对象会自动删除子对象。 父对象拥有对象的所有权, 在父对象被删除时会在析构函数中自动删除其子对象。
    ②、 手动删除子对象:当手动删除子对象时,会把该子对象从父对象的列表中移除, 以避免父对象被删除时该子对象被再次删除。 总之 QObject 对象不会被删除两次。
    ③、当一个对象被删除时,会发送一个 destroyed()信号,可以捕捉该信号以避免对 QObject对象的悬垂引用。
  7. 对象创建规则
    ①、 子对象通常应创建在堆中(使用 new 创建), 此时就不再需要使用 delete 将其删除了,当父对象被删除时,会自动删除该子对象。
    ②、 对于 Qt 程序,父对象通常创建在栈上,不应创建在堆上(使用 new 创建)
    ③、 子对象不应创建在栈中,因为若父对象比子对象更早的结束生命期(即父对象创建于子对象之后),则子对象会被删除两次,第一次发生在父对象生命期结束时, 由 Qt 对象树的规则,使用父对象删除子对象,第二次发生在子对象生命期结束时, 由 C++规
    则删除子对象。 这种错误可使用先创建父对象后创建子对象的方法解决,依据 C++规则,子对象会先被删除, 由 Qt 对象树规则知, 此时子对象会从父对象的列表中移除,当父对象结束生命期时,就不会再次删除子对象了。
  8. 其他规则:应确保每一个 QObject 对象在 QApplication 之后创建,在 QApplication 销毁之
    前销毁,因此 QObject 对象不能是 static 存储类型的,因为 static 对象将在 main()返回之后
    才被销毁,其销毁时间太迟了。
  9. 对象的名称:可以为每个对象设置一个对象名称,其主要作用是方便对对象树进行查询
    和管理。对象名称和对象是不同的,比如 A ma; 其中 ma 是对象,若为 ma 设置一个名称
    为“SSS”,则对象 ma 的对象名称为“SSS”
  10. 设置父对象的方法
    ①、 创建对象时,在构造函数中指定父对象, QObject 类及其子类都有一个形如 QObject
    *parent=0 的形参的构造函数,因此我们可以在创建对象时在构造函数中直接指定父
    对象。
    ②、使用 void QObject::setParent(QObject *parent)函数为该对象设置父对象。
  11. 设置对象名称:
    对象名称由 QObject 的 objectName 属性指定(默认值为空字符串),该属性的读取函数分别如下所示(注:对象的类名可通过 QMetaObject::className()查询。)
    Qstring objectName() const //读取该对象名称
    void setObjectName(const QString &name); //设置该对象的名称为 name

示例:对象树与对象的删除

#include "mainwindow.h"
#include <QApplication>
#include<iostream>
#include<QVector>
#include<QDebug>
using namespace std;
class A:public QObject{
public:
    A(){}
    A(QObject *p):QObject (p){}
    ~A()
    {
        qDebug()<<objectName()<<"=~A";
    }
};
class B:public QObject{
public:
    B(){}
    B(QObject *p):QObject (p){}
    ~B()
    {
        qDebug()<<objectName()<<"=~B";
    }
};
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    A ma;   // 父对象通常创建在栈上
    A *pa1 = new A(&ma);    //在堆上指定父对象
    A *pa2 = new A(&ma);
    B *pb1 = new B(pa1);
    B *pb2 = new B(pa2);
    ma.setObjectName("ma");
    pa1->setObjectName("pa1");
    pa2->setObjectName("pa2");
    pb1->setObjectName("pb1");
    pb2->setObjectName("pb2");
    A ma1;
    B mb1;
    mb1.setParent(&ma1); //在栈上把ma1 指定为mb1的父对象,此处父对象创建于子对象之前
    ma1.setObjectName("ma1");
    mb1.setObjectName("mb1");

    //下述错误,栈上指定父对象,父对象应创建于子对象之前
    //此处会导致子对象mb2,被析构两次,导致程序崩溃
    B  mb2;
    A ma2;
    //mb2.setParent(&ma2);

    w.show();
    qDebug()<<"---------------------------------------------------";
    return a.exec();
}

输出:

在这里插入图片描述

"" =~A    //ma2的析构
"" =~B     //mb2的析构
"mb1" =~B   //mb1的析构
"ma1" =~A     //ma1的析构
"ma" =~A       //ma的析构。其余是堆上创建的,系统自己回收
"pa1" =~A       //各对象负责删除自己的子对象
"pb1" =~B
"pa2" =~A
"pb2" =~B
  1. 查询对象树的信息,可使用以下 QObject 类中的成员函数
    ①、 QObject* parent() const 返回一个指向父对象的指针。
    ②、 const QObjectList& children() const
     作用:返回指向父对象中全部子对象的指针列表,新添加的子对象位于列表的最后(某些特定操作可改变该顺序,例如提升或降低 QWidget 子对象)。其中QObjectList 类型如下
    typedef QList<QObject*> QObjectList; //QList 是 Qt 的列表容器。
    ③、 QList findChildren ( const QString& name=QString(),Qt::FindChildOptions options=Qt::FindChildrenRecursively) const
     作用: 返回能转换为类型 T,且名称为 name 的所有子对象,若没有此对象,则返回空列表,若 name 为默认值,则会匹配所有对象的名称。该函数是按递归方式执行的。
     该函数与下面介绍的 findChild 的主要用途是可以通过父对象(或父部件)获取指向子对象(或子部件)的指针。
     name 参数:该参数是由 QObject::setObjectName 函数设置的名称。
     options 参数:该参数用于指定查询子对象的方式,该参数需要指定一个FindChildOption 类型的枚举值, FindChildOptions(注意,后面多了一个 s)是由Flags使用 typedef 重命名后的结果。 该参数可取以下两个FindChildOption 类型的枚举值
     Qt::FindDirectChlidrenOnly:表示查找该对象的直接子对象。
     Qt::FindChildrenRecursively:表示按递归方式查询该对象的所有子对象。
     注:经测试,当该对象没有名称为 name 的子对象时,该函数才返回空。
    ④、 T findChild(const QString& name=QString(),
    Qt::FindChildOptions options=Qt::FindChildrenRecursively) const
    该函数的作用与 findChildren 相同,但是该函数只能返回单个的子对象,
    ⑤、 void dumpObjectTree()
    该函数可在调试模式(debug)下输出某个对象的整个对象树结构。该函数在发布模式(release)下不会执行任何操作

示例:查询对象树

#include "mainwindow.h"
#include <QApplication>
#include<iostream>
#include<QVector>
#include<QDebug>
using namespace std;
class A:public QObject
{
public: A(){}
    A(QObject *p):QObject(p){}
    void f(){qDebug()<<"AF"; }
};
class B:public QObject
{
public:
    B(){}
    B(QObject *p):QObject(p){}
    void f(){qDebug()<<"BF";}
};
class C:public QObject
{
public: int c;
    C(){}
    C(QObject *p):QObject(p){}
    void f(){qDebug()<<"CF";}
};
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    A ma; A *pa1=new A(&ma); A *pa2=new A(&ma);
    B *pb1=new B(pa1); B *pb2=new B(pa1);
    C *pc1=new C(pb1); C *pc2=new C(pb1);
    ma.setObjectName("ma"); pa1->setObjectName("pa1"); pa2->setObjectName("pa2");
    pb1->setObjectName("pb1"); pb2->setObjectName("pb2");
    pc1->setObjectName("pc1"); pc2->setObjectName("pc2");
    pc2->c=2; pc1->c=1;
    QObjectList st= ma.children();
    qDebug()<<"~~~~~~~~~~~~~~~~~~~~~~~~~~";
    qDebug()<<"ma="<<ma.children();
    //以上输出: ma= (QObject(0x82d638, name = "pa1"), QObject(0x82d478, name = "pa2"))
    qDebug()<<"pb1="<<pb1->children();
    //以上输出: pb1= (QObject(0x840f38, name = "pc1"), QObject(0x840bf0, name = "pc2"))
    qDebug()<<"\n##### dumpObjectTree #####";
    ma.dumpObjectTree(); //输出见下图
    qDebug()<<"############";
    pb1->dumpObjectTree(); //输出见下图
    qDebug()<<"\n###### findChild #####";
    C* p1=ma.findChild<C*>("pc1"); //通过父对象 ma 获取指向子对象 pc1 的指针。
    p1->f(); //输出 CF
    qDebug()<<p1->c; //输出 1
    C* p2=ma.findChild<C*>("pc2",Qt::FindDirectChildrenOnly); /*获取ma的直接子对象中名称为pc2
    的对象的指针,因为 pc2 不是 ma 的直接子类,所以该处返回 NULL。 */
    //qDebug()<<p2->c; //错误,此时 p2指向的是NULL
    qDebug()<<"\n###### findChildren #####";
    QList<C*> s=ma.findChildren<C*>("pc1"); //Qt::FindDirectChildrenOnly
    qDebug()<<s; //输出: (QObject(0x840f38, name = "pc1"))
    qDebug()<<s[0]->c; //输出 1
    QList<C*> s1=ma.findChildren<C*>("pc2",Qt::FindDirectChildrenOnly);
    qDebug()<<s1; //输出一个空圆括号,因为 pc2 不是 ma 的直接子对象。
    //查找 ma 的直接子对象
    QList<C*> s2=ma.findChildren<C*>(QString(),Qt::FindDirectChildrenOnly);
    qDebug()<<s2; //输出: (QObject(0x82d638, name = "pa1"), QObject(0x82d478, name = "pa2"))
    QList<C*> s3=ma.findChildren<C*>(); //获取 ma 的整个对象树结构的列表
    qDebug()<<s3;

    w.show();
    qDebug()<<"---------------------------------------------------";
    return a.exec();
}

输出:
在这里插入图片描述

ma= (QObject(0x29041c74840, name = "pa1"), QObject(0x29041c74ca0, name = "pa2"))
pb1= (QObject(0x29043fb2b60, name = "pc1"), QObject(0x29043fb31c0, name = "pc2"))

##### dumpObjectTree #####
QObject::ma 
    QObject::pa1 
        QObject::pb1 
            QObject::pc1 
            QObject::pc2 
        QObject::pb2 
    QObject::pa2 
############
QObject::pb1 
    QObject::pc1 
    QObject::pc2 

###### findChild #####
CF
1

###### findChildren #####
(QObject(0x29043fb2b60, name = "pc1"))
1
()
(QObject(0x29041c74840, name = "pa1"), QObject(0x29041c74ca0, name = "pa2"))
(QObject(0x29041c74840, name = "pa1"), QObject(0x29041c74f20, name = "pb1"), QObject(0x29043fb2b60, name = "pc1"), QObject(0x29043fb31c0, name = "pc2"), QObject(0x29041c74890, name = "pb2"), QObject(0x29041c74ca0, name = "pa2"))

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

四、多线程服务器

1.进程的缺陷和线程的优点 1.进程的缺陷 创建进程&#xff08;复制&#xff09;的工作本身会给操作系统带来相当沉重的负担。 而且&#xff0c;每个进程具有独立的内存空间&#xff0c;所以进程间通信的实现难度也会随之提高。 同时&#xff0c;上下文切换&#xff08;Cont…

cmd:读取电脑硬件序列号

一、读取电脑硬件序列号 1.cmd 在没有使用第三方库的情况下&#xff0c;要读取电脑的硬件序列号通常需要使用操作系统提供的工具或命令行。以下是一个示例&#xff0c;展示如何使用Windows操作系统的命令行工具 wmic 来获取硬件序列号&#xff1a; 打开命令提示符&#xff0…

尚硅谷Flink(三)时间、窗口

1 &#x1f3b0;&#x1f3b2;&#x1f579;️ &#x1f3b0;时间、窗口 &#x1f3b2;窗口 &#x1f579;️是啥 Flink 是一种流式计算引擎&#xff0c;主要是来处理无界数据流的&#xff0c;数据源源不断、无穷无尽。想要更加方便高效地处理无界流&#xff0c;一种方式就…

【Linux学习笔记】代码编辑工具vim

1. vim工具基本模式的转换2. vim命令模式下的各种编辑命令2.1. 光标行定位2.2. 光标自由定位2.3. 复制粘贴2.4. 删除2.5. 文本的大小写替换2.6. 文本的替换2.7. 文本的前删后删2.8. 撤销操作 3. vim底行模式下的命令3.1. 设置行号与取消设置行号3.2. 分屏操作3.3. 在不退出vim的…

Postman简单使用

文章目录 一.接口测试流程二、Postman接口测试工具三、接口关联四、全局变量和环境变量 一.接口测试流程 拿到API接口文档&#xff08;从开发拿或者抓包获取&#xff09;&#xff0c;熟悉接口业务&#xff0c;接口地址&#xff0c;错误码等等 编写接口的测试用例以及评审 编写…

SSL证书续费要如何操作

SSL证书一旦到期&#xff0c;网站会立即无法访问&#xff0c;而且会提出不安全警告&#xff0c;如果是电商或者品牌网站影响还是很大的。 SSL证书和域名续费有很大区别&#xff0c;域名续费只要交钱就可以了&#xff0c;SSL证书续费还需要认证和更新服务器SSL证书文件才算收工…

【多线程】JUC(java.util.concurrent)的常见类 信号量 线程安全的集合类

目录 1. Callable接口 1.1 Callable接口和Runnable接口的区别&#xff1f; 1.2 使用Callable接口编写代码。 2. ReentrantLock 可重入锁 3.信号量 semaphore 3.1 Java中信号量的使用 4.CountDownLatch JUC: java.util.concurrent -> 这个包里的内容主要是一些多线程…

智能变电站自动化系统的应用与产品选型

摘要&#xff1a;现如今&#xff0c;智能变电站发展已经成为了电力系统发展过程中的内容&#xff0c;如何提高智能变电站的运行效率也成为电力系统发展的一个重要目标&#xff0c;为了能够更好地促进电力系统安全稳定运行&#xff0c;本文则就智能变电站自动化系统的实现进行了…

青藏高原连续日光诱导叶绿素荧光数据集(2000-2018)

简介&#xff1a; 青藏高原连续日光诱导叶绿素荧光数据集&#xff08;2000-2018&#xff09;是通过MODIS各通道反射率和SIF观测数据建立神经网络模型&#xff0c;从而得到较高时空分辨率的SIF数据&#xff0c;常作为初级生产力的参考。前言 – 人工智能教程 源数据范围为全球&…

网工实验笔记:MQC原理与配置

一、概述 MQC&#xff08;Modular QoS Command-Line Interface&#xff0c;模块化QoS命令行&#xff09;是指通过将具有某类共同特征的数据流划分为一类&#xff0c;并为同一类数据流提供相同的服务&#xff0c;也可以对不同类的数据流提供不同的服务。 MQC三要素 流分类&am…

15-k8s-高级存储之pv与pvc

文章目录 一、相关概念二、创建pv二、创建pvc三、创建pod调用pvc四、StorageClass动态制备pv 一、相关概念 关系 生命周期相关概念 2.1 静态构建&#xff1a;集群管理员创建若干PV卷。这些卷对象带有真实存储的细节信息,并且对集群用户可用(可见)。PV卷对象存在于Kubernetes …

摩尔信使MThings的设备高级参数

摩尔信使MThings支持三级参数管理方案&#xff0c;依次为&#xff1a;数据级、设备级、通道级。 设备级参数不仅包含设备名称、设备地址等常用信息&#xff0c;同时提供了诸多高级参数&#xff0c;其同样是为了满足不同用户应用场景中所面临的差异化需求&#xff0c;以更加灵活…

勒索病毒LockBit2.0 数据库(mysql与sqlsever)解锁恢复思路分享

0.前言 今天公司服务器中招LockBit2.0勒索病毒&#xff0c;损失惨重&#xff0c;全体加班了一天基本解决了部分问题&#xff0c;首先是丢失的文件数据就没法恢复了&#xff0c;这一块没有理睬&#xff0c;主要恢复的是两个数据库&#xff0c;一个是16GB大小的SQLserver数据库&…

安徽阳光心理测量平台目录遍历

安徽阳光心理测量平台目录遍历 FOFA指纹 title"心理测量平台"漏洞复现 路由后拼接/admin/UserFiles/ GET /admin/UserFiles/ HTTP/1.1 Host: {{Hostname}}修复方案 针对路径设定对应权限

注释的重要性与程序员的责任

注释的重要性与程序员的责任 提升代码可读性促进团队协作提高代码可维护性传承知识和经验代码的责任推荐学习 导语&#xff1a;在编写代码的过程中&#xff0c;注释是程序员们经常讨论的话题。有人认为忽视注释等于耍流氓&#xff0c;但也有人觉得注释只是浪费时间。本文将探讨…

软件开发项目文档系列之三如何撰写项目招标文件

前言 招标文件在采购过程中扮演着至关重要的角色&#xff0c;其主要目的是提供清晰而详尽的信息&#xff0c;以确保采购项目的需求得以明确&#xff0c;潜在的投标单位能够清晰理解并遵守相关要求&#xff0c;并最终为采购方提供一个有力的依据来评估和选择最合适的承建单位。…

c++之new和delete

前言 在本文中&#xff0c;您将学习使用new和delete操作在C 中有效地管理内存。 数组可用于存储多个同类型数据&#xff0c;但是使用数组存在严重的缺点。声明数组时应分配内存&#xff0c;但在大多数情况下&#xff0c;直到运行时才能确定所需的确切内存。在这种情况下&#…

python每日一练(8)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

微信小程序开发指南

前言 微信是一款由中国著名互联网公司腾讯公司开发的社交软件&#xff0c;于2011年1月21日正式上线。在成立后的短短几年时间里&#xff0c;微信以其简单易用的界面和强大的功能&#xff0c;快速赢得了全球用户的青睐。截止2021年&#xff0c;微信已经有超过10亿的活跃用户&am…

如何使用 OpenSSL 来检查证书,来确保网络通信的安全性?

OpenSSL 是一个强大的安全套接字层密码库&#xff0c;包含丰富的加密算法、常用的密钥和证书封装管理功能以及 SSL/TLS 协议&#xff0c;并提供了丰富的应用程序供测试或其他目的使用。要使用 OpenSSL 来检查证书以确保网络通信的安全性&#xff0c;您可以遵循以下步骤&#xf…