【C++】面向对象编程的三大特性:深入解析继承机制

news2024/9/23 13:38:58

在这里插入图片描述

C++语法相关知识点可以通过点击以下链接进行学习一起加油!
命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇
类和对象-下篇日期类C/C++内存管理模板初阶String使用
String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriority Queue与仿函数
模板进阶-模板特化

本文将深入解析面向对象编程的三大核心特性,特别是对继承机制的详细探讨。通过对这些概念的优化理解,帮助读者更好地掌握面向对象编程的精髓。

请添加图片描述
Alt
🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 一、前文
  • 二、继承
    • 2.1 继承概念
    • 2.2 继承关系和访问限定符
    • 2.3 继承基类成员访问方式间变化
    • 2.4 基类的private成员
      • 2.4.1 公共接口间接使用基类的私有变量
    • 2.5小结
    • 2.6 基类和派生类对象赋值转换
      • 2.6.1 派生类赋值基类
      • 2.6.2 基类对象不能赋值给派生类对象(建立在public继承)
      • 2.6.3 强制基类赋值派生类
    • 2.7 继承中作用域
      • 2.7.1 继承体系相关知识
      • 2.7.2 继承体系的隐藏
        • 2.7.2.1 成员变量隐藏
        • 2.7.2.2 成员函数隐藏
    • 2.8 派生类的默认成员函数
      • 2.8.1 默认构造函数
      • 2.8.2 拷贝构造
      • 2.8.3 赋值运算符重载
      • 2.8.4 析构函数(有坑)
      • 2.8.5 小结
    • 2.9 继承与友元
    • 2.8 继承与静态成员
  • 三、菱形继承及菱形虚拟继承
    • 3.1 继承分类
    • 3.2 菱形继承问题
    • 3.3 虚继承(菱形虚拟继承)
      • 3.3.1 虚继承概念
      • 3.3.2 虚拟继承解决数据冗余和二义性的原理
      • 3.3.3 多继承指针偏移特性
  • 四、继承总结和反思
    • 4.1 设计继承建议
    • 4.2 继承和组合
    • 4.3 低耦和与高内聚
  • 五、笔试面试题
  • 六、继承是子类拷贝一份父类数据吗?(重点)

一、前文

面向对象编程的三大特性:封装、继承和多态。

  1. 封装通过将数据和方法封装在对象中,提高了数据的安全性和代码的可维护性。
  2. 继承允许新类从现有类继承属性和方法,实现代码复用和扩展。
  3. 多态则通过统一的接口实现不同的行为,提高了代码的灵活性和扩展性。

封装:

  1. 数据和方法放到一起,把想给访问定义成公有,不想给你访问定义成私有和保护
  2. 一个类型放到另一个类型里面,通过typedef成员函数调整,封装另一个全新的类型相当于武器库

二、继承

2.1 继承概念

  • 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
  • 它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和方法
  • 每个派生类对象都是一个特殊的基类对象,派生类在基类的基础上进行扩展,增加新功能
  • 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
  • 以前我们接触的复用都是函数复用,继承是类设计层次的复用
using namespace std;

class Person
{
    public:
    void Print()
    {
        cout << "name:" << _name << endl;
        cout << "age:" << _age << endl;
    }
    private:
    string _name = "peter";//名字
    int _age = 18;//年龄
};

class Student : public Person
{
    protected:
    int _stuid;//学号
};


class Teacher : public Person
{
    protected:
    int jobid;//工号
};
int main()
{
    Student s;
    Teacher t;
    s.Print();
    t.Print();

    return 0;
}

从代码中可以看出Person是父类或基类,Student是子类或派生类

在这里插入图片描述

2.2 继承关系和访问限定符

在这里插入图片描述

2.3 继承基类成员访问方式间变化

在这里插入图片描述
注意】:圈出来的是实践中最常见的设计方式,主要了解这部分。

2.4 基类的private成员

基类private成员在派生类中无论以什么方式继承都是不可见的,这里的不可见是指基类的私有成员还是被继承到了派生类中,但是语法上限制派生类对象不管在类里面还是在类外面都是不能去访问

class Person
{
    public:
    void Print()
    {
        cout << "name:" << _name << endl;
        cout << "age:" << _age << endl;
    }
    protected:
    string _name = "peter";
    private:
    int _age = 18;
};

class Student : public Person
{
    public:
    void func()
    {
        cout << _name << endl;
        cout << _age << endl;
    }
    protected:
    int _stuid;
};

在这里插入图片描述

按照上面的意思,Student继承了Person属性和方法,但是由于_age是属于私有成员是不可见且不能被访问。

2.4.1 公共接口间接使用基类的私有变量

class Student : public Person
{
    public:
    void func()
    {
        //间接的使用
        Print();
    }
    protected:
    int _stuid;
};

由于基类private成员在派生类中式不能被访问的,如果基类成员不想在类外直接被访问,但是需要在派生类中能访问。这样子可以将基类private成员定义为protected成员,可以看出来保护成员限定符是因继承才出现的

思考:

  • 类可以使用关键字class或者struct,他们只是默认的继承方式不同,为什么不使用struct呢?

解释:

  • struct默认继承方式和访问限定符都是公有的
  • class默认继承方式和访问限定符都是私有的
  • 最好函数显式写出继承方式,偷偷摸摸的不好!

2.5小结

  • 基类的私有成员在子类都是不可见。基类的其他成员在访问方式(取最小的权限)== Min(成员在基类的访问限定符、继承方式)public > prrotected > private
  • 在实际中一般常用的是public继承,而不是protected/private继承,也不提倡使用protected/private继承,因为protected/private继承下来的成员都是只能在派生类的类里面使用,实际中扩展维护性不强

2.6 基类和派生类对象赋值转换

2.6.1 派生类赋值基类

每个派生类对象都是一个特殊的基类对象,派生类在基类的基础上进行扩展,增加新功能

class Person
{
    protected:
    string _name;
    string _sex;
    int _age;
};

class Student : public Person
{
    public:
    int _No; //学号
};

派生类对象可以赋值基类的对象/基类的指针/基类的引用。这里有个形象的说法叫切片或者切割。(将派生类中父类那部分切来赋值过去)。

在这里插入图片描述

int main()
{
    Student sobj;
	//子类对象可以赋值给父类对象
    Person pobj = sobj;
    //子类对象可以赋值给父类指针
    Person* pp = &sobj;
    //子类对象可以赋值给父类引用
    Person& rp = sobj;
}

2.6.2 基类对象不能赋值给派生类对象(建立在public继承)

int main()
{
    Student sobj;

    Person pobj = sobj;
    Person* pp = &sobj;

    sobj = pobj;
}

基类是不能直接赋值给派生类,因为可能派生类包含了基类没有的额外属性和方式,而且基类是不具备这些属性和方式,对此不能直接赋值。

2.6.3 强制基类赋值派生类

基类的指针或者引用可用通过强制类型转化赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。

int main()
{
    Student sobj;

    Person pobj = sobj;
    Person* pp = &sobj;

    1.0
    //pp存放这子类实例的地址,再进行强转
    pp = &sobj; 
    // 这种情况转换时可以的。
    Student* ps1 = (Student*)pp; 
    ps1->_No = 10;
	
    2.0
    //pp存放父类实例的地址
    pp = &pobj;
    // 这种情况转换时虽然可以,但是会存在越界访问的问题
    Student* ps2 = (Student*)pp; 
    ps2->_No = 10;
}

注意:

  • 以上提前需要满足是公有继承

  • 切割/切片是赋值兼容,编译器进行特殊处理,不产生临时对象,将子拷贝给父。内置类型值拷贝,自定义类型调用拷贝构造。

2.7 继承中作用域

2.7.1 继承体系相关知识

  1. 在继承体系中基类和派生类都有独立的作用域

  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的访问,这种情况叫隐藏(重定义)-在子类成员函数中,可以使用 基类::基类成员 显式访问

  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构造隐藏

  4. 注意在实际中在继承体系里面最好不要定义同名的成员,很容易混洗

2.7.2 继承体系的隐藏

提起作用域,就会想到域起到名字隔离的作用,不同的域可以有同样的名字,但是同一个域不能有同一个名字(函数重载比较特殊,但是变量不可以)

  • 全局/局部变量 - 影响生命周期
  • 类/命名空间域 -不影响生命周期
2.7.2.1 成员变量隐藏
class Person
{
public:
       
private:
    string _name = "xiaoming";
};
class Student : public Person
{
public:
    void Print()
    {
        cout << _name << endl;
    }
private:
    string _name = "zhangsan";
};
int main()
{
    Student st;
    st.Print();//张三

    return 0;
}

在这里插入图片描述

具体说明:

  • 关于上述代码,Student继承Person,导致Student中有两个_name。由于继承不是将父类数据拷贝一份给子类,实际上Student只有一个 _name还有一个是父类的
  • 这里父类和子类 _name构成隐藏关系,虽然代码可以跑,但是容易混洗,在继承体系中尽量不要定义同名的成员。
  • 这里会默认优先访问子类的 _name,如果想要访问父类中该成员,可以使用指定类域限定符。如果子类中没有该成员,将会去父类中查找
2.7.2.2 成员函数隐藏
class A
{
    public:
    void fun()
    {
        cout << "func()" << endl;
    }
};

class B : public A
{
    public:
    void fun(int i)
    {
        A::fun();
        cout << "func(int i)->" <<i<<endl;
    }
};
void Test()
{
    B b;
    b.fun(10);
};

这里这段代码说明一件事, B中的fun和A中的fun不是构成重载,因为不是在同一作用域。B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。

2.8 派生类的默认成员函数

在这里插入图片描述

在这里插入图片描述

2.8.1 默认构造函数

按照继承体系,子类中成员可以分为三类:子类内置类型成员、子类自定义类型成员以及父类成员,父类成员可以看出一个自定义类型的整体,这三类成员都会走初始化列表。

//父类
Person(const char* name = "peter")
    :_name(name)
    {
        cout << "Person()" << endl;
    }

//子类
Student(const char* name, int num)
    :Person(name)
        ,_num(num)
    {
        cout << "Student(int num)" << endl;
    }

具体说明:

在初始化步骤中,内置类型不进行处理,自定义类型会调用构造构造,父类也会调用自身的拷贝构造。

需要注意:

初始化列表中是否显式初始化父类成员,不影响父类成员走初始化列表,这里父类成员将调用复用自身的构造函数,需要考虑自身参数匹配的问题。这边建议父类类中显式写个全缺省构造函数,类的调用父类构造函数初始化,自己初始化自己的变量,也可以避免参数对应不上,父类尽量都走初始化列表。

容易错误的地方:

  • 不能单独对父类成员进行初始化,而是对父类这个整体,参数需要对应上。
  • 在创建 Student 对象时,首先使用 name 参数来构造 Person 部分的对象。这个过程并不会产生匿名对象,而是直接在 Student 对象中嵌入的 Person 部分。

2.8.2 拷贝构造

在这里插入图片描述

具体说明:

  • 切割/切片是赋值兼容,编译器进行特殊处理,不产生临时对象将子拷贝给父。内置类型值拷贝,自定义类型调用拷贝构造,父类复用父类的拷贝构造

2.8.3 赋值运算符重载

//父类
Person& operator=(const Person& p)
{
    cout << "Person& operator=(const Person& p)" << endl;
    if (&p != this) _name = p._name;
    return *this;
}
//子类
Student& operator= (const Student& s)
{
    cout << "Student& operator= (const Student& s)" << endl;
    if (this != &s)
    {
        Person::operator=(s);
        _num = s._num;
    }
    return *this;
}

具体说明:

  • 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,隐藏。对此这里按照父类复用父类的赋值运算符重载的话,需要使用指定类域限定符直接到父类中调用

2.8.4 析构函数(有坑)

//父类
~Person()
{
    cout << "~Person()" << endl;
}
//子类
~Student()
{
    Person::~Person();
    cout << _name << endl;
    cout << "~Student()" << endl;
} 

坑点:

  1. 问题一

    • 子类析构里面如果显式写父类的析构就有问题,按照规定构造是先父后子,析构是先子后父,为了析构顺序是先子后父,子类析构函数结束后会自动调用父类析构,如果出现在子类析构中显式调用父类析构,无法保证先子后父的规定。
  2. 问题二:

    • 同时如果显式调用父类析构,那么父类对象的内存空间已经被释放,此时再去访问父类成员将会导致未定义的醒为或者程序崩溃。并且在调用完子类的析构会自动调用再次调用父类的析构,这种行为是未定义的,可能会导致内存泄漏或者程序崩溃。

子类的析构也会隐藏父类,根据多态的需要,析构函数名字会被统一处理成destructor

2.8.5 小结

  • 派生类的构造函数必须调用基类的构造函数(复用)初始化基类的那一部分成员,如果基类没有默认的构造函数,那么必须在派生类构造函数的初始化列表显式调用
  • 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
  • 派生类的operator=必须调用基类的operator=完成基类的复制
  • ↑↑↑以上都是说子类继承父类的那一部分,需要父类调用复用自己默认函数
  • 派生类的析构函数会在调用完成后完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序
  • 派生类对象初始化先调用基类构造再调用派生类构造
  • 派生类对象析构清理先调用派生类再调用基类的析构

2.9 继承与友元

友元关系不能继承,也就是说基类友元一般不能访问子类私有和保护成员

class Student;
class Person
{
    public:
    friend void Display(const Person& p, const Student& s);
    protected:
    string _name; // 姓名
};

class Student : public Person
{
    protected:
    int _stuNum; // 学号
};

void Display(const Person& p, const Student& s)
{
    cout << p._name << endl;
    cout << s._stuNum << endl;
}
void main()
{
    Person p;
    Student s;
    Display(p, s);
}

父类的友元不一定是子类的友元(父亲的朋友不一定是你的朋友),意味着友元关系不能继承。如果想要访问子类,可以添加友元。这里编译器是从上到下扫描的,所以需要先声明Student类

2.8 继承与静态成员

**基类定义了static静态成员,则整个继承体系里面只有一个这样的成员,无论派生多少个子类,都只有一个static成员实例。**对此静态成员属于当前类,也属于基类。

那么利用该特性,子类的构造都需要调用父类的构造,可以统计父类有多少个派生类。

class Person
{
    public :
    Person () {++ _count ;}
    protected :
    string _name ; // 姓名
    public :
    static int _count; // 统计人的个数。
};

int Person :: _count = 0;

class Student : public Person
{
    protected :
    int _stuNum ; // 学号
};
class Graduate : public Student
{
    protected :
    string _seminarCourse ; // 研究科目
};
void TestPerson()
{
    Student s1 ;
    Student s2 ;
    Student s3 ;
    Graduate s4 ;
    cout <<" 人数 :"<< Person ::_count << endl;
    Student ::_count = 0;
    cout <<" 人数 :"<< Person ::_count << endl;
}

三、菱形继承及菱形虚拟继承

3.1 继承分类

单继承:当一个子类只有一个直接父类时称这个继承关系为单继承

在这里插入图片描述

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

在这里插入图片描述

菱形继承:属于多继承的一种特殊情况

在这里插入图片描述

在这里插入图片描述

3.2 菱形继承问题

从上面可以看出来,菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。

class Person
{
    public :
    string _name ; // 姓名
};
class Student : public Person
{
    protected :
    int _num ; //学号
};
class Teacher : public Person
{
    protected :
    int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
    protected :
    string _majorCourse ; // 主修课程
};
void Test ()
{
    // 这样会有二义性无法明确知道访问的是哪一个
    Assistant a ;
    a._name = "peter";
}

Assistant的对象中Person成员会有两份,那么如果单纯a._name = "peter"会产生二义性,编译器无法明确知道访问的是哪一个直接父类的成员。

解决办法:使用指定类域限定符访问

a.Student::_name = "xxx";
a.Teacher::_name = "yyy";

不足之处:需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决

3.3 虚继承(菱形虚拟继承)

3.3.1 虚继承概念

在这里插入图片描述

对此引入虚继承这个概念,可以用于解决菱形继承的二义性和数据冗余的问题。

class Person
{
    public :
    string _name ; // 姓名
};
class Student : virtual public Person
{
    protected :
    int _num ; //学号
};
class Teacher : virtual public Person
{
    protected :
    int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
    protected :
    string _majorCourse ; // 主修课程
};
void Test ()
{
    Assistant a ;
    a._name = "peter";
}

我们需要在腰部位置上将继承改为虚继承(使用virtual)

在这里插入图片描述

3.3.2 虚拟继承解决数据冗余和二义性的原理

这里需要借助内存窗口观察对象成员的模型,调试窗口有时为了方便观察进行了调整,导致结果不准确。

class A
{
    public:
    int _a;
};
// class B : public A
class B : virtual public A
{
    public:
    int _b;
};
// class C : public A
class C : virtual public A
{
    public:
    int _c;
};
class D : public B, public C
{
    public:
    int _d;
};
int main()
{
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    d._d = 5;
    return 0;
}  

从下列两个模拟来看,一个是没有虚继承导致数据冗余和二义性,一个是完成虚继承解决了数据冗余和二义性

在这里插入图片描述

具体说明:

  • 第一种,这里B和C类都存储了一个变量_a,导致数据冗余和二义性。
  • 第二种,这里可以看出D对象中将A放到了对象组成的最下面,这个_a同时属于B和C,修改这个 _a同样会影响到其他类的 _a。
  • 这里我们需要知道B和C如何去找到公共的A,这里是通过B和C的两个指针(就是属于x类内存中第一个存储的地址),去指向一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量(十六进制)可以找到下面的_a。

在这里插入图片描述

注意:

  • 内存存储的顺序是按照声明顺序或者继承顺序存储的。

3.3.3 多继承指针偏移特性

继承的指针偏移特性,为了支持动态绑定和多态性。多继承的情况下,由于不同的父类可能有不用的虚函数表,因此需要通过指针偏移来正确访问不同父类的虚函数表。这种指针偏移通常由编译器自动生成的,确保访问父类的成员或者调用父类的虚函数时,能够正确地定位到父类对象内存位置。

四、继承总结和反思

4.1 设计继承建议

一般不建议设计出多继承,一定不要设计出菱形继承,不然在复杂度及性能上都有问题。实践中可以设计多继承,但是切记不要设计菱形继承,因为太复杂,容易出现各种问题

4.2 继承和组合

  • public继承:是一种is-a的关系,也就是说每个派生类对象都是一个基类对象
  • 组合:是一种has-a的关系,假设B组合了A,每个B对象中都有一个A对象

继承允许你根据基类的实现派生类的实现。这种通过生成派生类的复用通常被称为为白箱复用(white-box reuse),术语"白箱"是相对可视性而言:在继承方式中,基类的内部细节对子类可见。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合性度高

对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组合或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以"黑箱"的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先实现对象组合有助于你保持每个类被封装

实践尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合

组合比如:

class A
{
private:
    int _ a;
}

//组合
class B
{
	private:
    A _aa;
    int _b;
}

在这里插入图片描述

4.3 低耦和与高内聚

  • 低耦和:类和类之间、模块与模块之间关系不那么紧密,关联不高
  • 高耦合:类和类之间、模块与模块之间关系很紧密,关联很高

五、笔试面试题

问题:

  1. 什么是菱形继承?菱形继承的问题是什么?
  2. 什么是菱形虚拟继承、解决数据冗余和二义性的?
  3. 继承和组合的区别?什么时候用继承?什么时候用组合?

回答:

  1. 菱形继承是指在面向对象编程中的一种继承关系,其中一个列同时继承两个不同的类,而这两个类又都继承自同一个父类,形成了一个菱形的继承结构。

  2. 数据冗余和二义性问题,并且还有代码维护困难、不稳定性

  3. 菱形虚拟继承是C++中用来解决菱形继承问题的一种机制,派生类通过虚拟继承来自共同基类,被虚继承的基类只会在继承层次结构中存在一份实例,而不会出现多次复制

  4. 继承和组合是面向对象编程中两种不同的关系模式,继承是一种"is-a"关系,表示的是类之间的一种分类关系,即子类是父类的一种特殊形式,而组合是一种"has-a"关系,表示一个类包含另一个类作为基一部分,但是它们之间不具有层次关系。

  5. 当存在明确的"是一个is-a"关系,且子类需要继承父类的行为和属性时,使用继承,当需要在不同的类之间建立层次关系时,使用继承。

  6. 当对象之间存在包含关系,一个对象包含另一个对象作为其一部分时,使用组合。当对象之间的关系更加动态,或者不需要建立明确的层次关系时,使用组合。

六、继承是子类拷贝一份父类数据吗?(重点)

在继承中,子类和父类之间是一种逻辑关系子类没有真正地拥有一份父类代码的副本,而是通过继承机制在需要时访问父类的功能。继承的这种特性使得子类不仅可以直接使用父类已有的功能,还可以通过重写(override)或扩展(extend)来修改或增加功能,而这一切都是通过共享父类的代码实现的,而不是通过复制代码实现的。

继承的实现是在运行时,通过类之间的关系来实现的。子类通过引用父类的成员,而不是复制父类的代码。因此,子类并没有物理上拥有父类的代码,而是通过继承关系“共享”父类的代码。

因此,继承不是拷贝,它是一种更高级的代码复用机制,通过类的层次结构来共享行为,而不是简单地复制粘贴代码。这样不仅可以减少代码重复,还可以通过多态和重写机制来增强代码的灵活性和可扩展性。


以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!
请添加图片描述

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

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

相关文章

近几年来说最有效率的编程语言和市场最认可的编程语言分别是什么?

在过去的几年中&#xff0c;编程语言的效率和市场认可度在不断演变。不同的语言适用于不同的领域和场景&#xff0c;因而编程语言的“效率”和“市场认可”需要根据具体应用来分析。本文将从两个角度入手&#xff0c;分别探讨近几年中被认为最有效率和最受市场认可的编程语言。…

MISC - 第二天(wireshark,base64解密图片,zip文件伪加密,LSB二进制最低位,ARCHPR工具)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解杂项 乌镇峰会种图 使用了stegsolve工具&#xff0c;查看更多信息 发现flag信息 更改为html后缀flag{97314e7864a8f62627b26f3f998c37f1} wireshark 看题目是 分析pacp数据包&#xff0c;通过网站登录…

模组差分包,可能是你远程升级失败的罪魁祸首!

也许我们已经习惯生活里的问题接连不断。。。但当收到客户的问题反馈&#xff0c;还是会心头一紧&#xff01; 最近有客户反馈在乡村里频繁出现掉线的情况。 我们赶紧排查&#xff1a;换货、换SIM卡&#xff0c;发现只有去年5月22号采购的那批模块在客户环境附近会出现掉线的…

2024年 人工智能领域的一些成果与未来发展趋势 形式丰富多样

目前人工智能领域发展迅速&#xff0c;在多个方面取得显著成果。2024 人工智能大会展览规模、首发新品数均达历史最高&#xff0c;有超过 500 家企业参展&#xff0c;展品数量超过 1500 项。 在教育领域&#xff0c;人工智能落地成果显著。只需一键扫描就可以自动批改试卷的作业…

基于yolov5滑块识别破解(一)

由于内容较长&#xff0c;将分为两个部分来说明&#xff0c;本文讲解yolov5的部署与训练。 1.YOLOv5部署 云端部署&#xff08;训练&#xff09; 服务器创建 如果自己的显卡算力不是很好的&#xff0c;或者是核显电脑&#xff0c;可以租用算力&#xff0c;价格还行一块钱左右就…

nodejs基于vue+express度假村旅游管理系统设计与实现7t82p

目录 功能介绍数据库设计具体实现截图技术栈技术论证解决的思路论文目录核心代码风格详细视频演示源码获取 功能介绍 实现了一个完整的农家乐系统&#xff0c;其中主要有用户表模块、关于我们模块、收藏表模块、公告信息模块、酒店预订模块、酒店信息模块、景区信息模块、景区…

96. UE5 GAS RPG 实现闪电链技能(一)

闪电链有一个施法的过程&#xff0c;就是在按键按下的过程&#xff0c;会在按下的过程一直持续造成伤害&#xff0c;一直等到条件不满足&#xff08;技能键位抬起&#xff0c;蓝量不足&#xff0c;被眩晕&#xff09;时&#xff0c;将结束技能&#xff0c;并退出技能状态。 所以…

以太坊客户端Geth的介绍与搭建

一、以太坊客户端 1.介绍 以太坊客户端是指用于连接、交互和参与以太坊区块链网络的软件。以太坊客户端允许用户执行各种操作&#xff0c;如发送交易、挖矿、部署智能合约、同步区块链数据等。 2.功能 区块链同步&#xff1a;客户端会下载并验证以太坊区块链的所有区块&…

7. 无线网络安全

7. 无线网络安全 (1) 无线网络面临的安全威胁 无线网络由于其开放性和无线传输的特性,面临着多种安全威胁,主要包括: 窃听:攻击者可以截获无线电信号并解析出数据,甚至在加密通信中收集加密信息用于以后的分析。 通信阻断:通过DoS攻击或其他干扰手段阻断通信,使设备间无…

TFT-LCD显示屏(1.8寸 STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.工作原理&#xff1a;TFT-LCD色彩空间 三、程序设计 main.c文件 lcd.h文件 lcd.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 TFT-LCD&#xff0c;全称Thin Film Transistor Liquid Crystal Display&a…

gitlab修改访问端口

目录 1.找到gitlab.rb文件&#xff0c;一般在/etc/gitlab/路径下 2.打开配置文件&#xff0c;加上代码 3.重新配置 4.重启gitlab 1.找到gitlab.rb文件&#xff0c;一般在/etc/gitlab/路径下 2.打开配置文件&#xff0c;加上代码 打开文件 sudo vi gitlab.rb 加上默认端口配…

苹果AI手机遇阻,国产手机找到超车机遇

行至九月&#xff0c;2024年&#xff0c;这个所谓AI手机的元年&#xff0c;已经走过近三个季度了。 市场最为期待的AI手机机型也基本都发布了。9月20日&#xff0c;首款搭载Apple Intelligence功能的苹果新品iPhone16正式发售。或许是为了进一步扩大销售&#xff0c;今年天猫A…

【JAVA开源】基于Vue和SpringBoot的甘肃非物质文化网站

本文项目编号 T 042 &#xff0c;文末自助获取源码 \color{red}{T042&#xff0c;文末自助获取源码} T042&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

计算机网络-小型综合网络的搭建涉及到无线路由交换安全

目录 1 拓扑架构 2 做项目的思路 3 做配置 3.1先做核心交换 3.2 防火墙的配置 4 ac 和ap 的配置 4.1 ac上配置安全的东西 5.1 测试​编辑 1 拓扑架构 要求看上面的图 2 做项目的思路 这张网很明显是一个小综合&#xff0c;设计到我们的无线交换&#xff0c;路由…

统信服务器操作系统【SSH登录常见问题】解决方案

方案适用于统信服务器操作系统D/E/A版。 文章目录 前言问题及解决方案问题一问题现象问题原因问题方案问题二问题现象问题原因问题方案问题三问题原因问题方案问题四问题现象问题原因问题方案问题五问题现象问题原因问题方案问题六问题现象问题原因问题方案前言 介绍日常使用s…

语音识别控制(软件、硬件)

1. 环境 python版本&#xff1a;3.11.9 2. 完整代码 import sqlite3 import time import wave # 使用wave库可读、写wav类型的音频文件 from funasr import AutoModel import sounddevice as sd import numpy as np from modelscope import pipeline, Tasks from pypinyin …

软件著作权登记所需要的材料

软件著作权登记所需材料全面解析 在当今数字化时代&#xff0c;软件著作权作为保护软件开发者智力劳动成果的重要法律手段&#xff0c;其登记过程显得尤为重要。 一、软件著作权登记申请表 首先&#xff0c;软件著作权登记需要提交的最基本材料是《软件著作权登记申请表》。这份…

深度优先搜索算法及其matlab程序详解

#################本文为学习《图论算法及其MATLAB实现》的学习笔记################# 深度优先搜索算法(DepthFirst Search),简记DFS算法,是图论中的首要算法,其思想方法渗透到图论中的许多算法之中,尤其是DFS算法在求生成树、割点、块和平面图嵌入算法中起着极为关键的作用。…

写文档-画UML图-编程的秘密武器:Kimi智能助手

在快速发展的软件开发领域&#xff0c;如何高效地编写需求分析文档、软件设计文档以及代码&#xff0c;成为每位程序员和架构师面临的重要挑战。今天&#xff0c;我要向大家介绍一款强大的工具——Kimi智能助手&#xff0c;它将帮助你提升工作效率&#xff0c;优化开发流程。 …

【图灵完备 Turing Complete】游戏经验攻略分享 Part.5 编程

编程部分的话&#xff0c;第一关会让你输入机器码&#xff0c;这一章节还是比较简单的&#xff0c;因为操作码是固定给出的&#xff0c;只需要根据题意去编写&#xff0c;完成这章目的是为了解锁下面的关卡。 输入&#xff0c;移动COPY之后进行运算&#xff0c;然后输出。 激光…