【c++深入系列】:类与对象详解(上)

news2025/4/2 2:22:17

🔥 本文专栏:c++
🌸作者主页:努力努力再努力wz

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

💪 今日博客励志语录你仰望的星辰并非遥不可及,而是跋涉者脚印的倒影;你向往的远方未必需要翅膀,只要脚下始终有路,心中永远有光。

那么从本篇文章开始,c++就正式进入类和对象篇章了,而前面的内容我主要讲解围绕c++是兼容了c语言但是如何在c语言的基础上进行改善得到一些新的内容,比如命名空间以及函数的重载,那么之前的文章我们还可以找到c语言的一些影子,但是从这篇文章往后,c语言便和c++便逐渐分道扬镳了,我们终于能够见识到属于c++自己的特性,那么废话不多说,让我们进入正文的学习

面向对象

想必在学习c++之前,各位读者便听说这个名词,也就是面向对象,那么c++与c语言最大的不同便是c++是一门面向对象的语言,而c语言是一门面向过程的语言,那么此时就引出了本文的第一个疑问

什么是面向对象?什么是面向过程?

那么假设你现在要写一个程序,比如完成一个图书管理系统,那么如果你是用c语言来实现,也就是用面向过程的思想来解决的,那么此时你完成该图书管理系统你就会这么思考:

那么首先你第一步会先分析此时用户使用该图书管理的一个需求,比如用户使用图书管理系统无非就是去查询某本书的位置或者说要借阅或者归还某本书等等,那么分析出了这些需求之后,那么下一步,你便关注的是我解决这些需求涉及到的步骤,比如我要借阅某本书,那么第一步便是我得在图书管理系统中先注册用户身份然后登录,第二步则是我得查询该书是否在图书馆中,那么一旦查询到在该图书馆中,那么下一步便是可以借阅,所以你分析得到完成一个图书的借阅总共需要三个步骤,分别是先注册然后查询最后借阅,那么确地好步骤之后,那么下一步你将这个过程中的每一个步骤或者说动作分别定义一个函数来表示这个模块,比如注册定义一个函数,查询又定义一个函数,所以该需求的完整实现就是底层分别调用这几个函数即可

那么根据上面的例子,我们便能知道面向过程的一个思想就是我解决一个事物,我关注的是我解决事物的一个步骤,那么我们再来看看面向对象是怎么思考来完成图书管理系统的

那么面向对象的第一步同样也是先分析需求,那么用户用图书管理系统无非就是借阅以及查询等,然后分析完需求之后,此时面向对象关注的不再是解决这个需求的具体过程或者说步骤,而是关注此时涉及到的对象,比如借阅某本书,那么涉及到就是借阅者以及图书这两个对象,那么确定好对象之后,那么此时我们会将这两个对象给描述定义出来,比如这里的借阅者以及图书,那么定义出来之后,这些对象有自己的行为以及属性,那么让这两个对象之间进行交互即可实现该需求

那么根据上面的例子,我们便能理解面向对象的一个思想,那么以面向对象的方式来思考解决某个问题或者需求时,那么此时你关注的就是这个问题涉及到哪些对象,以及这些对象之间是怎么进行交互的,那么这就是面向对象。

所以现在我们学的是c++,所以以后我们得用面向对象的方式来写程序来解决问题,那么第一步我们就得知道在c++中我们如何去描述定义一个对象,而本文所讲的类便是用来描述对象。

什么是类

那么要学会面向对象编程的第一步便是得知道如何描述如何定义对象,而在我们现实生活中,我们知道我们向陌生人介绍你的朋友或者家人的时候,那么你如何让陌生人脑海中能够对你描述的人有一个画面或者认识,那么你采取的方式就是提取你要介绍的人的特征,比如他的年龄以及他的长相和工作等等,那么这些特征我们可以称之为一个人的属性

所以我们不管是描述人还是动物还是其他任何事物,那么描述的关键就是得先提取出这个事物的属性,那么对于程序员来说,程序员是怎么描述一个事物的呢,同样他也得提取出事物的属性,只不过这些属性比如年龄或者性别以及身高在计算机世界中的映射就是一个int类型或者char类型的数据,那么这些属性的集合就能够描述一个实体,那么c语言就是通过结构体来实现其内部封装不同类型的数据,比如定义一个struct person

struct person
{
    int age;
    char* name;
    char sex;
};

当然c语言的结构体明显不能完整的描述一个对象,不然c语言为什么是面向过程的语言而不是面向对象的语言

之所以c语言的struct结构体无法完整的描述一个对象,是因为它只能描述对象的特征,但是无法描述对象的行为,而面向对象的核心就是对象之间的交互,那么对象之间要交互肯定对象就得具备所谓的行为,所以你只有属性的话,那么你定义出的对象只是一个静态的对象,所以这里c语言的struct结构体就做不到的,但c++的类便可以做到了

那么c++的类也是来描述对象,它内部封装了各种不同的类型的数据也就是像struct结构体那样,但是其中还能封装成员函数,没错,还能封装函数,那么这就是c语言的struct结构体无法做到的,而函数意味着就是该对象的行为,所以c++的类的构造是由成员变量+成员函数所构成

类的定义以及实例化

1.类的定义

那么接下来我们得知道如何定义类,那么我们要定义类,我们就得使用class关键字后面跟上类名,其中我们要注意的是,类是一个域,此前我的c++系列的第一篇文章讲命名空间的时候就说过,c++的域分为几种,分别是局部域以及全局域还有命名空间域以及这里的类域,并且我们访问一个变量的时候,那么编译器获取变量的定义则是从该变量所处的局部域往外搜索直到全局域,默认不会搜索类域以及命名空间域,但是如果要访问这两个域的内容,就得用域作用限定符::指定访问,但是这里如果指定访问类域中的变量会有一个坑,我这里埋一个伏笔,我在下文会讲解,那么我们先来看看一个类的定义:

class person
{
    public:
     int age;
    int sex;
    void hello()
    {
        cout<<"hello c++"<<endl;
    }
};

那么我们仔细看这个类的定义,发现我们光是知道class关键字还不够,其中还有个什么public,那么这个public又是什么呢:

public是一个访问限定符

那么现在读者对访问限定符的疑问无非就两种,访问限定符是什么?有什么用?

1.访问限定符是什么?

那么先来回答第一个问题,也就是访问限定符是什么,访问限定符是C++中用于控制类成员访问权限的关键字,通过它们可以限制类中成员变量和成员函数被访问的范围,实现数据封装的核心机制。共有三种类型:

  • public(公有):成员在类内、外部均可直接访问。
  • protected(保护):成员在类内和派生类(子类)中可访问,类外不可直接访问。
  • private(私有):成员仅在类内可访问,类外及派生类中均不可直接访问。

那么现阶段也就是初学者阶段来说,其中我们就知道以及掌握public以及private的访问权限即可,而protected的使用场景现阶段还用不到,而类中的访问权限修饰的范围则是:

作用域起点:从访问限定符(public、protected、private)声明的位置开始,后续成员均受该限定符约束。

作用域终点:直到遇到下一个访问限定符或类的定义结束为止。

2.访问限定符有什么用?

那么我们知道被private修饰的成员变量以及成员函数只能在类中被访问,所以如果说我们定义了一个类,而对于该类来说,我们不希望该类的属性轻易被别人给访问到或者修改,那么我们可以将其用private修饰,但是如果外部还是有访问类里面的成员变量的需求,那么我们可以在类中定义一个public的函数,那么对方虽然无法直接获取到类中的属性,但是可以通过类向外部提供的接口也就是提供成员函数来访问到

class person
{
    private:
    int age;
    char sex;
    char* name;
    public:
    int getage()
    {
        return this->age;
    }
    int getsex()
    {
        return this->sex;
    }
};

而我们常说的面向对象三大特性:封装,继承和多态,那么访问限定符就涉及到其中所谓的封装思想,我们可以举一个例子来理解,比如银行,那么银行内部肯定封装了有各种客户的现金,但是银行本身不会说客户有取钱的需求,那么就直接让客户进入银行的金库中,自己自觉去取钱,因为银行本身不信任客户,但是却要满足客户的需求,所以它会建立一个窗口,那么窗口对面就是客户服务人员来接收用户的响应,然后由银行自己的工作人员去取钱给客户而不是客户亲自去取,那么这就是封装思想的其中一个体现,就是保证了安全性

其次像我们的电脑以及汽车也是封装思想的其中一个体现,那么我们开车的人是不需要掌握汽车内部的构造以及发动机是怎么运转的,只需要知道如何使用汽车的方向盘以及刹车等汽车暴露给我们的接口如何使用即可,而这里封装思想的另一个体现就是它能够封装内部复杂的实现细节,给外部用户提供简单便捷的接口来使用

所以我们发现c++要实现这些封装的思想,那么一定离不开类的访问限定符

所以有了访问限定符之后,我们就知道如何定义一个简单的类了,那么就是用class关键字然后内部封装成员变量以及成员函数,然后再结合访问限定符给修饰限定成员变量以及成员函数

而如果我们没有显示的指定访问限定符,比如:

class person
{
    int age;
    char sex;
    void hello()
    {
        cout<<"hello"<<endl;
    }
};

那么此时默认该类的成员函数以及成员变量则默认设置为private修饰,而在c++中也将结构体升级为了类,也就意味着在c++中结构体可以定义成员变量以及成员函数,但是对于结构体如果我们不显示给访问限定修饰符,那么默认权限是public

2.类的实例化

我上文埋了一个小的伏笔,我们知道类也是一个作用域,而我们访问一个变量的内容的时候,那么编译器会从变量所处的局部域往外搜索直到全局域来搜索该变量的定义,那么不会默认搜索命名空间域以及类域,如果变量的定义在命名空间域的话,则需要域作用限定符指定访问,但是如果在类域中的话,是否也能通过域作用限定符来指定访问呢?

那么验证真理的最好方法便是实验,那么我们可以写一段简单的c++代码来实验一下,代码的逻辑很简答,定义了一个类然后用上文所说的方式来指定访问:

#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    void hello()
    {
        cout<<"hello"<<endl;
    }
};
int main()
{
    cout << person::age << endl;
    return 0;
}

在这里插入图片描述

那么发现编译阶段就报错了,那么为什么会报错呢?

那么是因为类域中包含的非静态成员变量只是声明而不是定义,那么既然只是声明的话,那么此时它不会被分配内存空间,那么我们是无法指定来访问它的内容的,而这里我前面很严谨的加了一个非静态的成员变量,那么至于被static修饰的静态成员变量又是怎么样的情况,那么我会在之后的文章中讲解,所以我这里先挖一个坑

所以类可以理解为一个房子的建造图纸,那么你要带着你的家人去参观房子的卧室以及客厅,那么肯定是带你的家人去参观按照这个图纸建造好的房子,那么其中建造好的房子会为卧室以及客厅分配空间,那么你不可能带你的家人来看图纸来参观

所以同理,我们要访问到类中的变量,那么就得先按照蓝图来建造房子,那么这个过程就是实例化,那么类的实例化和c语言的struct结构体实例化是一样的:

class person
{
public:
    int age;
    char sex;
    void hello()
    {
        cout<<"hello"<<endl;
    }
};
int main(){
//实例化
person a;
    return 0;
}

那么对象的实例化就包括成员变量的内存分配,那么我们就可以通过实例化出来的a对象来访问比如a对象中的成员变量a:

#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    void hello()
    {
        cout << "hello" << endl;
    }
};
int main()
{
    person a;
    a.age = 10;
    cout << a.age << endl;
    return 0;
}

在这里插入图片描述

那么其中我们给该对象中的成员变量赋值的话那么是和c语言结构体是一样的

a.age=10;
cout<<a.sex<<endl;

类的相关细节补充

那么我们掌握了类的定义以及类的实例化之后,那么我们知道了要访问类中的非静态成员变量,那么只能通过该类的实例化出来的对象来访问,那么现在我们来计算一下按照我们上文定义的person类实例化出来的对象的大小,来看看实例化的对象的构造

#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    void hello()
    {
        cout << "hello" << endl;
    }
};
int main()
{
    person a;
    cout << sizeof(a) << endl;
    return 0;
}

运行截图:
在这里插入图片描述

那么我们发现计算出的结果是8,那么计算出的结果为什么是8呢?

那么是因为对象内部只包含了两个成员变量分别是int类型的age以及char类型的sex,那么对象分配的内存空间也和c的struct结构体一样遵循内存对齐,那么我们来简单复习一下内存对齐的规则,那么内存对齐就是每一个成员变量在该对象中相对于起始位置的偏移量是按照ret(ret=min(编译器默认对齐数,该成员变量的数据类型的大小))的整数倍来对齐,而第一个成员变量起始位置就在对象的起始位置,那么这里vs下的默认对齐数是4,而第一个成员变量是int,那么其起始位置就和该对象的起始位置重合,那么分配4个字节的空间,而下一个成员变量sex是char类型,那么其数据类型的大小与默认的对齐数进行比较得到就是1,但是由于内存对齐规则规定对象整体的大小必须是最大对齐数的整数倍,而这里的最大对齐数是4,所以sex的起始位置的偏移量就是8个字节,然后其中分配一字节的空间给sex存储,所以计算得到该对象的大小是8个字节
在这里插入图片描述

而内存对齐的意义是因为CPU要从内存中读取数据,而从内存中获取的数据要从数据线传递给CPU,而数据线有32位,那么也就意味着CPU一次只能读4个字节的数据长度,而读取的效率就取决于读取的次数,那么如果不内存对齐,那么如果成员变量是挨着的话,那么此时CPU假设只要读取其中一个成员变量的数据,那么一次读取4个字节长度的数据,那么其中就会包含其他的成员变量的数据,那么它不是完整的数据,那么有了内存对齐就能完整访问数据并且减少可读取次数


所以通过这个结果我们知道实例化的对象中只给成员变量分配了空间而没有给成员函数分配空间,为什么这么设计的原因其实也容易理解,因为当我们实例化了一个对象之后,那么我们不一定就要调用该对象的所有的成员函数,所以没必要给成员函数开辟空间,别说成员函数了,其实我们的普通函数不也是这样设计的吗,当我们没有调用普通函数的时候,不会为其开辟栈帧,那么只有当调用普通函数的时候,才会为其在栈上开辟空间,所以这里实例化的对象中只包含成员变量其实是符合我们的预期的

那么我们就可以得出一个结论,那么同一个类实例出的不同的对象,那么他们是共享同一个成员函数的代码段的,那么这个结论我们可以定义不同的对象,然后调用同一个函数,然后查看该代码对应的汇编代码,那么会发现不同对象调用同一个成员函数对应的汇编指令,也就是call指令后面call的地址都是一样的,而后面call的地址就是成员函数的代码段所在的地址,那么博主确实整不来在vs上展示汇编码,无法给大家展示,读者可以下来自己实验一下


那么我们现在知道了同一个类实例化的不同对象是共享成员函数的,那么想必读者一定会有一个疑问,那么假设我在类中定义的成员函数会访问成员变量,那么虽然成员函数是共享的,但是成员变量却是每一个对象自己独立拥有的

那么现在问题就来了:

那么不同的对象调用同一个成员函数,那么该成员函数怎么知道该成员变量是属于那一个对象的呢?

那么其实c++的祖师爷肯定页想到了这一点,那么其实在类中的成员函数的参数列表中,会默认设置一个参数,那么该参数就是指向该对象的this指针,那么它是隐式设置的,也就是说我们看不到,它是在编译阶段生成的符号表中记录该成员函数的符号时自动添加该参数也就是this指针,所以当我们用对象调用成员函数的时候,那么会隐式的传递一个this指针,所以当我们成员函数中访问了成员变量,其实是通过this指针来解引用访问成员变量的

//你的视角:
class person
{
    public:
    int age;
    char sex;
    char* name;
    void hello()
    {
        cout<<"hello"<<endl;
    }
    void setage(int x)
    {
        age=x;
    }
    
};
//编译器的视角:
class person
{
    public:
    int age;
    char sex;
    char* name;
    void hello()
    {
        cout<<"hello"<<endl;
    }
    void setage(person& this,int x)
    {
        this->age=x;
    }
    
};

其中注意的是,这个this指针的参数是隐式设置的,意味着,我们不能显示的定义所谓的this指针或者显示的传this指针参数,那么这些工作都是编译器自动帮你完成好的,那么就不需要我们画蛇添足了,那么我们只能在成员函数体内部显示的调用this指针

//错误示范
#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    char* name;
    void hello()
    {
        cout << "hello" << endl;
    }
    void setage(person& this,int x)
    {
        this->age = x;
    }

};
int main()
{
    person a;
    a.setage(&a,18);
    cout << a.age << endl;
    return 0;
}

在这里插入图片描述

//正确示范
#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    char* name;
    void hello()
    {
        cout << "hello" << endl;
    }
    void setage(int x)
    {
        this->age = x;
    }

};
int main()
{
    person a;
    a.setage(18);
    cout << a.age << endl;
    return 0;
}

在这里插入图片描述

那么其中this的作用还不至于此,那么当我们在成员函数定义的形参与成员变量同名的时候,我们也可以用this指针加以区分,这样能避免混淆,正确赋值,但是希望大家永远不会遇到这样的场景,因为我们希望类的成员变量的命名尽可能避免这点,也就是与形参名冲突

class date
{
    public:
    int year;
    int day;
    int month;
    void setyear(int year)
    {
        this->year=year;
    }
};

那么知道了this指针之后,我们再来看一下与this指针有关,并且非常容易出错的使用场景

#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    char* name;
    void hello()
    {
        cout << "hello" << endl;
    }
    void printage(int x)
    {
        cout<<age<<endl;
    }

};
int main()
{
     person* a=nullptr;
     a->hello();
    return 0;
}

那么根据上面的代码,那么此时我就有一个问题,那么就是如果运行上面的代码,那么此时读者你认为上面的代码的运行结果是什么?

A.编译错误

B.运行崩溃

C.正常运行

那么我跑一下这段代码,来揭晓一下答案:
在这里插入图片描述

那么正确答案是c,那么不知道你是否答对呢

那么我相信三个选项肯定都有读者来选,其中选第一个选项的读者,我猜测他们应该是这样思考的,因为此时代码中定义了一个指向person对象的一个指针,但是初始化的时候将其设置为指向为空,那么我们接下来解引用该指针,而该指针是空,没有指向任何对象,那么觉得编译器会报一个空指针解引用的编译错误,而之所以这么思考,就是因为它认为当初c语言就要求:我们要定义一个指针的时候,在解引用之前一定要初始化指向正确的对象,否则会出现空指针解引用或者野指针问题

那么如果选A的读者是这么思考的,那么我想说的就是,你的理解部分是对的,错的原因就是不了解c++中用对象的指针调用成员变量以及成员函数的区别以及不熟悉c++的编译器检查的严格程度

首先确实我们在c语言中,指针一定要初始化指向对象,但是这里我们这里指针为空,为什么能够成功运行成员函数的原因就是当我们用指针调用类中的成员函数的时候,由于成员函数不存在对象中存在,对象中只存在成员变量,而成员函数的代码则是在代码段中,所以这里通过指针去访问成员函数就和调用普通函数是一样的,只不过这里不会解引用该指针,而是直接调用成员函数并且将该指针作为实参传递给形参this指针,所以这里能够成功运行hello成员函数(这里还有一个伏笔,我会在下文说到)。

//你的视角:
a->hello();
//编译器的视角:
person::hello(a);

所以我们如果我们用指针a去访问成员变量比如age成员变量时,那么此时便会运行崩溃,那么原因就很简单,因为age成员变量的定义实在对象当中,只有当实例化出对象,那么才会为成员变量分配空间,所以这里如果用指针去访问成员变量,那么就会和c语言一样,那么它会解引用指针,然后访问对象中的成员变量,那么就会出现空指针解引用

#include<iostream>
using namesapce std;
class person
{
    .......
};
int main()
{
    person* a=nullptr;
    a->age=10;
    cout<<a->age<<endl;
}

在这里插入图片描述

那么如果你看懂了上面的场景之后,那么我再来稍加修改,那么你现在应该能够正确识别该场景下的结果了:

那么这次我调用的不是hello成员函数而是printage成员函数

#include<iostream>
using namespace std;
class person
{
public:
    int age;
    char sex;
    char* name;
    void hello()
    {
        cout << "hello" << endl;
    }
    void printage()
    {
        cout<<this->age<<endl;
    }

};
int main()
{
     person* a=nullptr;
     a->printage();
    return 0;
}

A.编译错误

B.运行崩溃

C.正常运行

那么对于该代码的运行结果的你认为是上面的那三个选项中的哪一个,那么这次相信各位读者应该不会出错了

在这里插入图片描述

那么答案选B,也就是运行崩溃了

此时我们用person指针a调用成员函数printage函数,那么编译器会将a指针隐式传递给形参this指针,然后由于这里在函数体内部要访问成员变量age,那么必然要解引用this指针,而this指针是一个空指针,那么解引用空指针必然导致程序崩溃,那么vs下c++的编译器的检查不够严格,此时虽然解引用了空指针,还是通过了编译但是最终导致运行崩溃,而c的编译器在检查空指针的解引用这方面就很严格,那么在编译阶段就阻止你空指针的解引用。那么这个场景也是关于this指针非常易错常考的一个陷阱了,那么希望你下一次遇到这种场景不会跳进坑里面去

结语

那么这就是本篇关于类和对象的讲解,那么类和对象的各种内容以及细节还没有结束,那么我打算做三期博客来讲解类和对象的知识点,那么我持续更新,希望你能够多多关注,如果本文有帮组到你的话,还请三连加关注哦,你的支持就是我创作的最大的动力!
在这里插入图片描述

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

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

相关文章

6、进程理论和简单进程创建

一、了解进程推荐看这个视频&#xff0c;很详细 1、概念 进程(Process)程序的运行过程&#xff0c;是系统进行资源分配和调度的独立单元程序的运行过程&#xff1a;多个不同程序 并发&#xff0c;同一个程序同时执行多个任务。 就需要很多资源来实现这个过程。 每个进程都有一…

Todesk介绍

文章目录 ToDesk 软件介绍1. 软件概述2. ToDesk 的功能特点2.1 简单易用2.2 高质量的图像与流畅的操作2.3 跨平台支持2.4 多屏显示与协作2.5 文件传输功能2.6 实时聊天与语音通话2.7 远程唤醒与自动启动2.8 多种权限设置与安全性2.9 无需公网 IP 3. ToDesk 的应用场景3.1 个人使…

Linux 进程3-fork创建子进程继承父进程缓冲区验证

目录 1. fork创建子进程继承父进程缓冲区验证 1.1 write向标准输出&#xff08;终端屏幕&#xff09;写入数据验证 1.1.1 write函数写入数据不带行缓冲刷新 \n 1.1.2 write函数写入数据带行缓冲刷新 \n 1.2 fork创建前执行printf函数 1.2.1 fork创建前执行printf函数带\n…

应用服务接口第二次请求一直pending问题

目录 一、问题背景二、问题排查过程三、解决方案四、总结 一、问题背景 升级内容发布到灰度环境&#xff0c;验证相关服务&#xff0c;查看接口调用日志&#xff0c;发现第一次请求正常&#xff0c;第二次相同接口请求就一直pending&#xff0c;其他服务也是如此 二、问题排查…

基于FPGA的ESP8266无线数据传输(温湿度DTH11、光照强度BH1750、WIFI模块)连接中国移动onenet云平台,仿真+上板

文章目录 一、创建云平台产品设备二、FPGA仿真WIFI模块通信过程仿真分析2.上板 总结 一、创建云平台产品设备 使用串口助手测试传输过程 相关信息记录 二、FPGA仿真WIFI模块通信过程 仿真分析 //各个状态tx_dataalways (posedge clk or negedge rst_n) beginif(!rst_n) beg…

5款视觉OCR开源模型

一、号称「世界上最好的 OCR 模型」Mistral OCR Mistral OCR 擅长理解复杂的文档元素&#xff0c;包括交错图像、数学表达式、表格和高级布局&#xff08;如 LaTeX 格式&#xff09;。该模型可以更深入地理解丰富的文档&#xff0c;尤其是包含图表、图形、公式和数字的科学论文…

计算机二级(C语言)考试高频考点总汇(三)—— 结构体和共用体、结构体对齐规则、联合体大小计算

目录 九、结构体和共用体 十、结构体对齐规则 十一、联合体大小计算 九、结构体和共用体 141. 结构体是&#xff08;不同类型成员的集合&#xff09;&#xff0c;是⼀种用户自定义的数据类型&#xff0c;可以将不同类型的成员组合在⼀起&#xff0c;用于表示&#xff08;复…

Charles抓HTTPS包

一、电脑端 1、证书下载与安装 安装完之后&#xff0c;重新点开看一看&#xff0c;确认下证书状态&#xff0c;安装的没问题 2、charles设置 抓电脑端要把这个点开 3、抓包 正经人看浏览器的包一般是F12&#xff0c;不过这里就用浏览器代替电脑软件了 如果配制好charles之后…

JavaScript模板字符串:

1.示例代码&#xff08;包含注释&#xff09;: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>JS-数…

【系统架构设计师】数据库系统 ③ ( 数据库设计过程 | 概念结构设计 | ER 图 简介 | 概念设计阶段 工作拆分 )

文章目录 一、数据库设计过程 概述二、ER 图 简介1、ER 图 概念2、ER 图 示例3、ER 图 关系类型① 一对一 ( 1:1 ) 关系② 一对多 ( 1:n ) 关系③ 多对多 ( n:n ) 关系 三、概念设计阶段 工作拆分 一、数据库设计过程 概述 数据库设计过程 : 需求分析阶段 : 明确 用户需求 ; …

Servlet开发与生命周期详解-2

一、在集成开发环境当中开发Servlet程序 1.集成开发工具很多&#xff0c;其中目前使用比较多的是&#xff1a; IntelliJ IDEA&#xff08;这个居多&#xff0c;IDEA在提示功能方面要强于Eclipse&#xff0c;也就是说IDEA使用起来比Eclipse更加智能&#xff0c;更好用。JetBrai…

将网络安全和第三方风险管理与业务目标相结合

在网络安全风险领域&#xff0c;我们经常遇到与企业语言不通的问题。这可能导致网络安全风险管理计划得不到支持。当发现网络安全风险时&#xff0c;困难在于以符合组织语言和目标的方式来表达它。 第三方风险属于另一个灰色地带。在组织内部&#xff0c;许多利益相关者&#…

NO.58十六届蓝桥杯备战|基础算法-枚举|普通枚举|二进制枚举|铺地毯|回文日期|扫雷|子集|费解的开关|Even Parity(C++)

枚举 顾名思义&#xff0c;就是把所有情况全都罗列出来&#xff0c;然后找出符合题⽬要求的那⼀个。因此&#xff0c;枚举是⼀种纯暴⼒的算法。 ⼀般情况下&#xff0c;枚举策略都是会超时的。此时要先根据题⽬的数据范围来判断暴⼒枚举是否可以通过。 使⽤枚举策略时&#xf…

Python正则表达式(二)

目录 六、re.findall()函数和分组 1、0/1分组情况 2、多分组情况 七、或“|”的用法 1、作用域 2、用法 八、贪婪模式和懒惰模式 1、量词的贪婪模式 2、量词的懒惰模式 九、匹配对象 1、相关函数 六、re.findall()函数和分组 1、0/1分组情况 在正则表达式中&#x…

图解AUTOSAR_SWS_FlashDriver

AUTOSAR Flash驱动(FLS)模块详解 AUTOSAR基础软件存储抽象层组件详细解析 目录 1. 概述 1.1. Flash驱动模块简介1.2. 功能和作用2. 架构设计 2.1. 模块架构2.2. API接口设计2.3. 状态机设计2.4. 异步操作时序2.5. 配置结构2.6. 任务处理流程3. 总结 3.1. 设计优势3.2. 应用场景…

哪吒汽车:一边熬夜蹦迪,一边找药投医

两年前&#xff0c;威马CEO沈晖发了个短视频&#xff0c;内容是“活下去&#xff0c;像牲口一样活下去”。 如今最能体会沈晖当时心情的&#xff0c;估计就是方运舟了。 作为哪吒汽车创始人兼董事长&#xff0c;他连续多次被限高&#xff0c;为了让哪吒汽车活下去&#xff0c…

Linux一步部署主DNS服务器

​ #!/bin/bash #部署DHCP服务 #userli 20250319if [ "$USER" ! "root" ]then echo"错误&#xff1a;非root用户&#xff0c;权限不足&#xff01;"exit 0fi#防火墙与高级权限 systemctl stop firewalld && systemctl disable firewalld…

图片隐私清理工具

图片隐私清理助手&#xff1a;一键清除图片敏感信息的神器 在数字时代&#xff0c;我们每天都会拍摄和分享大量图片&#xff0c;但你是否注意过这些图片中可能暗藏隐私信息&#xff1f;相机的GPS定位、拍摄参数等EXIF数据&#xff0c;都可能在不经意间泄露你的隐私。今天介绍的…

【UE5】摄像机晃动

目录 效果 步骤 一、游戏中晃动视角 二、Sequence中晃动视角 效果 步骤 一、游戏中晃动视角 1. 新建一个蓝图&#xff0c;父类选择“CameraShakeBase” 这里命名为“BP_MyCameraShake” 打开“BP_MyCameraShake”&#xff0c;根晃动模式这里设置为“Perlin噪点摄像机晃…

类和对象—继承(1)

目录 1、继承1.1、继承的概念1.2、继承的语法 2、子类访问父类成员2.1、子类中访问父类的成员变量2.2、子类中访问父类的成员方法2.3、super 关键字 3、子类构造方法 1、继承 在 Java 中&#xff0c;类对现实中的实体进行描述&#xff0c;而类实例化的对象用来表示现实中的实体…