类 —— 友元、常/静态成员函数

类的大小

和结构体大小求法一致。但需注意,普通空类也会占用 1 字节大小,因为普通空类可以实例化对象。
在这里插入图片描述

而 抽象空类占 4 字节(32 位机中),因为抽象空类中含有虚指针(含有虚函数的非抽象空类同理)。
在这里插入图片描述

友元

某些情况下,需要频繁读写类的数据成员,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。引入友元。

友元函数

全局函数作为类的友元函数

用到哪一个类中的私有成员,就把全局函数的“声明”前加上 friend,放到哪一个类中就可以了。
全局函数的“声明”可以放置到类中任何位置,不受权限修饰符的影响
为避免不必要的麻烦,全局函数写在类的声明下面。一个全局友元函数理论上来说可以访问多个类。

#include <iostream>
#include <math.h>
using namespace std;

class N;

class M
{
    float x;
public:
    M() { }
    M(float x):x(x) { }
    friend double func(M m, N n);		// 声明全局函数 func 是 M 的友元函数
                	// 全局函数的“声明”可以放置到类中任何位置,不受权限修饰符的影响
};

class N
{
    float y;
public:
    N() { }
    N(float y):y(y) { }
    friend double func(M m, N n);		// 声明全局函数 func 是 N 的友元函数
                	// 全局函数的“声明”可以放置到类中任何位置,不受权限修饰符的影响
};

double func(M m, N n)
{
    return sqrt(m.x*m.x + n.y*n.y);
}

int main()
{
    M m(3);
    N n(4);
    cout << "√3²+4² = " << func(m, n) << endl;

    return 0;
}

在这里插入图片描述

一个类中成员函数作为另一个类的友元函数

如果 M 类中的函数,是 N 类的友元函数(由 N 声明),则 M 中的函数可以访问 N 中的私有成员。
1、需要把类做前置声明,但是类的前置声明仅说明可以使用类定义变量/形参,并不能声明类中的成员。
2、如果一个类中的成员函数,作为另一个类的友元,该类的成员函数必须在类内声明,类外定义

#include <iostream>
using namespace std;

class N;

class M
{
    float x;
public:
    M() { }
    M(float x):x(x) { }
    void show(N n);			// 想要此函数作为 N类 的友元函数,须类内声明,类外定义
};

class N
{
    float y;
public:
    N() { }
    N(float y):y(y) { }
    friend void M::show(N n);		// 把 M 中的 show 函数,声明为了 N 中的友元
};

void M::show(N n)			// 因为此函数是其他类N的友元,所以要在类外定义
{
    cout << "It's a function belongs to class M. " << endl;
    cout << "But it can use variables of class N: " << n.y << endl;
                        						// 访问另一个类 N 中的私有成员
}

int main()
{
    M m;
    N n(4);
    m.show(n);				// M 中的函数可以访问 N 中的私有成员

    return 0;
}

在这里插入图片描述

友元类

如果 M类 中,声明了 friend class N,则称 M类 把 N类 作为友元类。N类 中的所有成员都能访问 M类 中的私有成员。
如果 N类 中,声明了 friend class M,则称 N类 把 M类 作为友元类。M类 中的所有成员都能访问 N类 中的私有成员。
如果声明友元类,就不涉及到类中成员的问题,写代码时不需要考虑类的先后顺序。但后写的类要提前声明。

#include <iostream>
#include <math.h>
using namespace std;

class N;

class M
{
    float x;
public:
    M() { }
    M(float x):x(x) { }
    void show(N n);
    void show();
};

void M::show()
{
    cout << "This is a function belongs to class M: " << this->x << endl;
}

class N
{
    float y;
public:
    N() { }
    N(float y):y(y) { }
    friend class M;
};

void M::show(N n)
{
    cout << "This is a function belongs to class M. " << endl;
    cout << "But it can use variables of class N: " << n.y << endl;
}

int main()
{
    M m(3);
    N n(4);
    m.show(n);

    cout << endl;
    m.show();

    return 0;
}

在这里插入图片描述

💡 练习

定义两个类 Dog 和 Cat 类,分别具有私有的成员属性:颜色、性别、年龄。
写出两个类的无参构造和有参构造,定义一个全局函数:计算猫和狗的年龄之和,并输出。
定义 Dog 类为 Cat 类的友元,在 Dog 类中定义一个 c_show 函数,输出猫和狗的颜色。

#include <iostream>
using namespace std;

class Dog;

class Cat				// 必须定义在 Dog 前
{
    string color;
    string gender;
    int age;
public:
    Cat()
    {
        cout << "A constructor of cat without arguments." << endl;
    }
    Cat(string color, string gender, int age):color(color), gender(gender), age(age)
    {
        cout << "A constructor of cat with arguments." << endl;
    }
    friend int sum(Dog &d, Cat &c);
    friend class Dog;
};

class Dog
{
    string color;
    string gender;
    int age;
public:
    Dog()
    {
        cout << "A constructor of dog without arguments." << endl;
    }
    Dog(string color, string gender, int age):color(color), gender(gender), age(age)
    {
        cout << "A constructor of dog with arguments." << endl;
    }
    friend int sum(Dog &d, Cat &c);
    void c_show(Cat &c);
};

void Dog::c_show(Cat &c)
{
    cout << "The cat is " << c.color<< "." << endl;			// Cat 必须定义在前面的原因
    cout << "The dog is " << this->color << "." << endl;
}

int sum(Dog &d, Cat &c)
{
    return d.age + c.age;
}

int main()
{
    Dog d("husky", "female", 5);
    Cat c("ragdoll", "male", 6);

    cout << "Age: " << sum(d, c) << endl;
    d.c_show(c);

    return 0;
}

在这里插入图片描述

友元的注意事项

1、类的前置声明,只能表明存在该类,并不能说明类中有哪些成员。
2、如果一个类的成员函数作为另一个类的友元,需要在该类内部声明,在该类外部定义。
3、友元是单向的,A 声明了 B 为友元类,B 中的成员可以访问 A 中的私有成员。但这并不意味着 A 中的成员,可以访问 B 中的私有成员。
4、友元没有传递性。A中:friend class B,且 B中:friend class C ≠ A 把 C 当做友元类
5、提高了程序的运行效率,但是破坏了类的封装性和隐藏性,使得非成员函数也能够访问类中的私有成员。导致程序的维护性变差,因此使用友元要慎重

A中:friend class B ⇔ A 声明了 B 为友元类 ⇔ A 把 B 当做友元类 ⇔ B 是 A 的友元类 ⇔ B 可访问 A

常成员变量 & 常局部变量

常成员变量

该成员变量的值无法被修改。
常成员变量的初始化方式有两种:
● 直接赋值
声明后赋值:
● 构造初始化列表
上述两种方式同时使用时,前者失效,以后者为准。

#include <iostream>
using namespace std;

class Demo
{
private:
    // ● 定义时直接赋值
    const int a = 1;
    const int b = 2;
    const int c = 3;

public:
    // ● 构造初始化列表的方式进行初始化
    Demo(int a, int b, int c):a(a), b(b), c(c) {}

//    Demo(int a, int b, int c)
//    {
//        this->a = a;
//        this->b = b;
//        this->c = c;
//    }

    void show()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "c = " << c << endl;
    }

    void test()		// 错误
    {
//        a++;
//        b++;
//        c++;
    }
};

int main()
{
    Demo d(10, 20, 30);			
    d.show();

    return 0;
}

常局部变量

该局部变量不可被修改,常用于引用参数。

#include <iostream>
using namespace std;

class Demo
{
public:
    void test()
    {
        int a = 1;
        const int b = 2;
        a++;
//        b++; 			// 错误
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
    }

};

int main()
{
    Demo d;
    d.test();

    return 0;
}

常成员函数 & 常对象

对比普通常函数:const int add(int n1, int n2); ——> const 修饰函数的返回值

常成员函数

格式
返回值类型  成员函数名() const;
性质

1、常成员函数内,不能修改成员变量;
2、可以保护成员变量不被随意修改;
3、如果常成员函数的声明和定义分开,两个位置都要写上 const 修饰
4、不能调用非 const 的成员函数;
建议只要成员函数不修改成员变量就使用 const 修饰,例如 get 函数。

#include <iostream>
using namespace std;

class Demo
{
private:
    int a;
public:
    Demo(int a)
    {
        this->a = a;
    }

    void show()
    {
        cout << "哈哈哈哈哈" << endl;
    }

    // const 修饰的成员函数
    int get_a() const
    {
        return a;
    }

    void test() const
    {
        cout << get_a() << endl;
//        show(); 			// 错误
//        a++; 				// 错误
        cout << "a = " << a << endl;
    }
};

int main()
{
    Demo d(1);
    cout << d.get_a() << endl;
    d.test();
    
    return 0;
}

在这里插入图片描述

常成员函数的 this 指针
const  类名  *const  this;

this 的指向和值均不能更改。
在这里插入图片描述

mutable(慎用)

取消常属性,在类中给成员变量加上 mutable 关键字,就允许在常成员函数内修改成员变量。

#include <iostream>
using namespace std;

class M
{
    mutable float x;		// mutable 取消成员变量的常属性,可以在常成员函数中修改该成员变量
public:
    M() { }
    M(float x):x(x) { }
    void set_value(float val)const;
    void show();
};

void M::set_value(float val)const
{
    this->x = val;
}

void M::show()
{
    cout << this->x << endl;
}

int main()
{
    M m(12);
    cout << "Before: ";
    m.show();
    m.set_value(5);
    cout << "After: ";
    m.show();

    return 0;
}

在这里插入图片描述

常对象

实例化对象时,前面加上 const 关键字修饰。
常对象的性质:
1、常对象只能调用常成员函数(默认调用的特殊成员函数除外);
2、非常对象既可以调用非常成员函数,也可以调用常成员函数;
3、常对象可以调用成员变量,但不能修改;
在这里插入图片描述

#include <iostream>
using namespace std;

class M
{
    mutable float x;
public:
    M() { }
    M(float x):x(x) { }
    void set_value(float val)const;
    void show();
    void show(M m);
};

void M::set_value(float val)const
{
    this->x = val;
}

void M::show()
{
    cout << this->x << endl;
}

void M::show(M m)
{
    cout << m.x << endl;
}

int main()
{
    const M m1(12);
    m1.set_value(13);

    M m2(5);
    m2.show(m1);
    m2.show();
    m2.set_value(361);
    m2.show();

    return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;

class Demo
{
private:
    int a;
public:
    int b = 10;
    Demo(int a)
    {
        this->a = a;
    }

    void show()
    {
        cout << "哈哈哈哈哈" << endl;
    }

    // const修饰的成员函数
    int get_a() const
    {
        return a;
    }

    void test() const
    {
        cout << get_a() << endl;
//        show(); 		// 错误
//        a++; 			// 错误
        cout << "a = " << a << endl;
    }
};

int main()
{
    // 两种初始化方式,两种等效
    const Demo d(1);
//    Demo const d(1);
    
    // 常对象可以调用 const 修饰的成员函数。
    cout << d.get_a() << endl;

    // 常对象不可以调用 非const 修饰的成员函数。
//    d.show();

    // 错误
//    d.b = 11;
    // 可以调用成员变量,但是不能做修改
    cout << d.b << endl;
    return 0;
}

在这里插入图片描述

💡 练习

封装圆类 Circle,私有属性半径 R,圆周率 PI,在程序运行过程中 PI 的值不变,写出圆的构造函数,公有的求面积函数。

#include <iostream>
#include <iomanip>
using namespace std;

class Circle
{
    float radius;
//    mutable double PI;					// 方法一
    const double PI = 3.14159265359;		// 方法二,仅此一句
public:
    Circle() { }
    Circle(float radius):radius(radius) { }
    double area(Circle &cir)
    {
        return cir.radius * cir.radius * PI;
    }
    // void set_pi()const					// 方法一
    // {
    //     PI = 3.14159265359;
    // }
};

int main()
{
    Circle cir(2);
//    cir.set_pi();							// 方法一
    cout << setprecision(8) << "S = " << cir.area(cir) << endl;

    return 0;
}

在这里插入图片描述

静态局部变量、静态成员变量 & 静态成员函数

静态局部变量在第一次调用时创建,直到程序结束后销毁,同一个类的所有对象共用一份静态局部变量。
静态成员函数 和 静态成员变量,既不依赖类的对象而存在,也不占用类的空间。

静态局部变量(类的函数中:static 数据类型 变量名;)

#include <iostream>
using namespace std;

class Test
{
public:
    void func()
    {
        int a = 1;				// a可能会在同一个地址反复创建销毁。
        static int b = 1; 		// 静态局部变量,同一个类的所有对象共用一份
        cout << "a = "<< ++a << "\t" << &a << endl;
        cout << "b = "<< ++b << "\t" << &b << endl;
    }

};

int main()
{
    Test t1;
    t1.func();

    Test t2;
    t2.func();

    t1.func();
    t2.func();

    return 0;
}

在这里插入图片描述

静态成员变量(类中:static 数据类型 成员名;)

1、静态成员变量 独立于类体存在;
2、每一个类对象公用同一个静态成员;
3、即使没有实例化类对象,也可以使用类中的静态成员;
4、静态成员一般是 public 权限;
5、静态成员需要在全局处声明,声明的同时可以进行初始化操作,不初始化默认为 0;
6、静态成员不占用类的大小;
● 在程序开始运行时就开辟内存,直到程序运行结束销毁。
● 虽然静态成员变量可以使用对象调用,但是更建议直接使用类名进行调用。
静态成员函数
1、不依赖于任何类对象(可以在没有类对象的情况下调用);
2、静态成员函数 没有 this 指针(因为可以不通过类对象被调用);
3、静态成员函数,不能直接使用非静态成员变量; ——> 间接使用方法见链接 (👈 链接至另一博主,放心跳转)
4、调用方式:① 直接通过类名加域限定符调用,② 通过类对象调用;
5、非静态成员函数和静态成员函数可以构成重载;
6、不能在静态成员函数中,调用同类中其他非静态成员函数;
7、若要 类内声明类外定义,声明 需要写 static 关键字,类外的实现不用写;

#include <iostream>
using namespace std;

class Rectangle
{
public:
    static float height;
    float width;
    static void show()
    {
        //cout << "width = " << width << endl;      // 3、
        cout << "height = " << height << endl;
    }
    void show(float width)      // 5、
    {

    }
};

float Rectangle::height;   		// 需要在全局处声明,可以初始化,也可以不初始化(默认为0)

int main()
{
    Rectangle::show();          //  1、4 ①
    cout << Rectangle::height << endl;
    Rectangle rec1;
    rec1.height = 80;
    Rectangle rec2;
    rec2.show();                    // 4 ②
    cout << rec2.height << endl;
    cout << "rec1.height\t" << &(rec1.height) << endl;
    cout << "rec2.height\t" << &(rec2.height) << endl;
    cout << sizeof(Rectangle) << endl;
    
    return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;

class Test
{
public:
    void func0()
    {
        cout << "void func0()" << endl;
        func1();
    }

    // 静态成员函数
    static void func1()
    {
        cout << "static void func1()"<< endl;
        func2();
    }

    static void func2()
    {
        cout << "static void func2()" << endl;
//        func0(); 		// 6.错误,静态成员函数,不能调用非静态成员函数
    }
};


int main()
{
    // 类名直接调用
    Test::func1();

    Test t1;
    t1.func0();
    t1.func2();

    return 0;
}

在这里插入图片描述

如果要在静态成员函数内调用非静态成员的属性方法,可以通过参数将对象传进来。

#include <iostream>
using namespace std;

class Test
{
public:
    void func0()
    {
        cout << "void func0()" << endl;
//        func1();
    }

    // 静态成员函数
    static void func1(Test &t)
    {
        t.func0();
        cout << "static void func1(Test &t)"<< endl;
        func2();
    }

    static void func2()
    {
        cout << "static void func2()" << endl;
//        func0(); // 错误,静态成员函数,不能调用非静态成员函数
    }
};


int main()
{
    // 类名直接调用
//    Test::func1();

    Test t1;
    t1.func0();
    t1.func1(t1);
    t1.func2();

    return 0;
}

在这里插入图片描述

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

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

相关文章

Vue指令之v-html

在Vue中有很多特殊的标签属性&#xff0c;这些属性一般以’v’开头&#xff0c;用于在标签中实现特殊的功能。 例如&#xff0c;当Vue实例的data是一个inner html&#xff0c;我们想在网页上渲染这部分html&#xff0c;如果依然使用之前的{{ variable }}&#xff0c;则只会将i…

排序算法:n个0~1000之间的整数,将他们从大到小排序

上榜理由&#xff1a; 如果没见过这种排序题&#xff0c;可能首先想到的就是常用的排序算法&#xff0c;比如快速排序&#xff0c;归并排序&#xff0c;那如果输入的n足够大&#xff0c;时间复杂度肯定比较高。其实题目0-1000的范围是一个题眼&#xff0c;所以一定有更优的排序…

【鸿蒙应用ArkTS开发系列】- 选择图片、文件和拍照功能实现

文章目录 前言创建多媒体Demo工程创建MediaBean 实体类创建MediaHelper工具类API标记弃用问题动态申请多媒体访问权限实现选择图片显示功能打包测试 前言 在使用App的时候&#xff0c;我们经常会在一些社交软件中聊天时发一些图片或者文件之类的多媒体文件&#xff0c;那在鸿蒙…

网络安全--基于Kali的网络扫描基础技术

文章目录 1. 标准ICMP扫描1.1使用Ping命令1.1.1格式1.1.2实战 1.2使用Nmap工具1.2.1格式1.2.2实战1.2.2.1主机在线1.2.2.2主机不在线 1.3使用Fping命令1.3.1格式1.3.2实战 2. 时间戳查询扫描2.1格式2.2实战 3. 地址掩码查询扫描3.1格式3.2实战 2. TCP扫描2.1TCP工作机制2.2TCP …

拆解按摩器:有意思的按键与LED控制电路,学习借鉴一下!

拆解 外观和配色个人感觉还行,比较青春 拉开拉链&#xff0c;拆开外面的布面&#xff0c;里面还有一层纱面 按键部分使用魔术贴固定 拆开纱面后&#xff0c;看到里面的结构&#xff0c;整体是一个海绵 可以看到如下&#xff0c;电池&#xff0c;按键板&#xff0c;充电线的三条…

【Java】文件路径-绝对路径与相对路径

1、绝对路径与相对路径 先来看一下绝对路径和相对路径的定义&#xff1a; 绝对路径是指完整的描述文件位置的路径就是绝对路径。如Windows系统中的D:\Project\data\test.txt&#xff0c;MAC系统中的/Users/liuwenwen/Desktop/Project/test.txt 相对路径是指相对于当前文件位置…

[操作系统] 面试宝典之~死锁连环系列

文章目录 2.22 什么是死锁2.24 解决死锁的方法死锁的预防死锁的避免死锁的检测死锁的解除 2.22 什么是死锁 在多道程序环境下&#xff0c;多个进程可以竞争有限数量的资源。当一个进程申请资源时&#xff0c;如果这时没有可用资源&#xff0c;那么这个进程进入等待状态。有时&…

什么是高级语言、机器语言、汇编语言?什么是编译和解释?

1、高级语言 计算机程序是一种让计算机执行特定任务的方法。程序是由程序员用一种称为编程语言的特殊语言编写的。编程语言有很多种&#xff0c;例如 C、C、Java、Python 等。这些语言被称为高级语言&#xff0c;因为它们更接近人类的自然语言&#xff0c;而不是计算机能够直接…

【数据结构 —— 二叉树的链式结构实现】

数据结构 —— 二叉树的链式结构实现 1.树的概念及其结构1.1.树概念1.2.树的结构1.3树的相关概念1.4.树的表示1.5. 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2.二叉树的概念及其结构2.1二叉树的概念2.2.现实中的二叉树&#xff1a;2.3. 特殊的二叉树…

C++单调向量(栈):好子数组的最大分数

作者推荐 利用广度优先或模拟解决米诺骨牌 题目 给你一个整数数组 nums &#xff08;下标从 0 开始&#xff09;和一个整数 k 。 一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i1], …, nums[j]) * (j - i 1) 。一个 好 子数组的两个端点下标需要满足 i < k <…

32+OLED之IIC手撕亚索七级狗牌

成品效果&#xff1a; 硬件配方&#xff1a;STM32F103C8T6 SSD1306 软件配方&#xff1a;字模提取V2.1 CopyLeft By Horse2000 STM32CubeMX keil5 思路&#xff1a; 1.找到图片&#xff0c;将其转化为128*64 像素大小的二值化图片&#xff1b;【python实现转化】&#xff…

配置zabbix-proxy主动式

IP地址对应关系如下&#xff1a; zabbix-server122.9.8.21zabbix-proxy122.9.4.102zabbix-agent2116.63.9.109 一、 安装zabbix-server https://blog.csdn.net/qq_50247813/article/details/132131774 二、 安装zabbix-proxy a. 安装zabbix源 rpm -Uvh https://repo.zabbix…

机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割

一、简述 虽然Segment Anything用于图像分割的通用大模型看起来很酷(飞桨也提供分割一切的模型),但是个人感觉落地应用的时候心里还是更倾向于飞桨这种场景式的,因为需要用到一些人体分割的需求,所以这里主要是对飞桨高性能图像分割开发套件进行了解和使用,但是暂时不训练…

Modbus平台:协议中间件(支持Modbus TCP、RTU、ASCII)

该程序可放置外网中&#xff0c;适用于DTU长连接&#xff08;心跳包必须包含DTU&#xff0c;可以是tcp/udp&#xff09;&#xff0c;也可以在内网中&#xff0c;短连接访问设备server 支持协议&#xff1a;Modbus TCP | RTU | ASCII 连接方式&#xff1a;TcpAtive: TCP主动 | …

机器人制作开源方案 | 智能扶老助残辅助管家

作者&#xff1a;孙运通 黄善越 卢瑀 张宇峰 郑乐怡 单位&#xff1a;河海大学 指导老师&#xff1a;陆其清 人口老龄化始终是我国一个极为严峻的社会问题。独居老人和空巢老人占总人口比重日益提高&#xff1a;预计至2050年老龄人口占比将超20%&#xff0c;绝大部分城市和地…

基于单片机环境监测温湿度PM2.5系统设计

**单片机设计介绍&#xff0c;基于单片机环境监测温湿度PM2.5系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 设计一个基于单片机环境监测温湿度PM2.5的系统是一个非常有意义的项目。以下是一个基本的介绍&#xff1a; …

云原生实战课大纲<2>

我们pod的数据挂载文件可以使用 pv-pvc的方式 1. 创建pv池 2. 在pv池中创建pv&#xff0c;并且设置pv的模式 3. 编写pod 写对应的pvc 申请书 就可以了这就是我们k8s中的pv和pvc 基于pv池创建pv的时候会有容量限制呢么关于配置呢&#xff0c;我们以前会有这种场景 比如说在dock…

Android Studio导入项目一直显示正在下载Gradle项目

如题&#xff0c;问题图类似如下&#xff1a; &#xff08;此图是解决以后截的&#xff0c;之前遇到问题没截图&#xff09; 解决方法 先找到你正在下载的gradle的版本是哪个 然后在链接中 ​​​​​​Gradle Distributions 找到你所对于gradle的版本&#xff0c;下载对应…

Centos7安装配置nginx

快捷查看指令 ctrlf 进行搜索会直接定位到需要的知识点和命令讲解&#xff08;如有不正确的地方欢迎各位小伙伴在评论区提意见&#xff0c;小编会及时修改&#xff09; Centos7安装配置nginx Nginx介绍 Nginx (engine x) 是一个高性能的 HTTP 和 反向代理 服务&#xff0c;也…

Spring Security 6.x 系列(6)—— 显式设置和修改登录态信息

一、前言 此篇是对上篇 Spring Security 6.x 系列&#xff08;5&#xff09;—— Servlet 认证体系结构介绍 中4.9章节显式调用SecurityContextRepository#saveContext进行详解分析。 二、设置和修改登录态 2.1 登录态存储形式 使用Spring Security框架&#xff0c;认证成功…