C++ | C++11知识点

news2025/4/5 14:02:40

        前言: 本篇内容讲述了C++11中比较重要的内容为:右值引用、可变参数模板、lambda表达式和包装器。
         ps:包装器博主在另一篇文章讲述的,本篇文章包装器板块为原文链接。

C++11知识点目录

  • 花括号初始化
    • 自定义类型的花括号初始化
    • 内置类型的花括号初始化
    • 多参数自定义类型的花括号初始化
  • auto和decltype
  • 范围for
  • 右值引用
    • 左值和右值
    • 左值引用和右值引用的相同点和不同点
      • 相同点
      • 不同点
    • move
    • 右值引用的作用
    • 移动语义
  • 万能引用
  • final和override
    • final
    • override
  • 可变参数模板
    • 概念
    • sizeof
    • 参数包的扩展
    • 理解包扩展
    • 完美转发
  • lambda表达式
    • lambda的基本语法
    • 捕获方式
    • mutable
    • 代码演示
  • 包装器

花括号初始化

c++11添加了任意类型都可以使用花括号进行初始化

自定义类型的花括号初始化

#include <iostream>
#include <string>
#include <vector>
int main()
{
	std::string s1 = "nihao";
	std::string s2 = "niyehao";
	std::vector<std::string> vec1 {	s1, s2 };
	std::vector<std::string> vec2 = { s1, s2 };
	return 0;
}

内置类型的花括号初始化

#include <iostream>
int main()
{
	int k { 1 };
	int arr[] {1, 2, 3, 4, 5, 6, 7, 8};
	std::cout << k << std::endl;
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		std::cout << arr[i] << " ";
	}
	std::cout << std::endl;
	return 0;
}

多参数自定义类型的花括号初始化

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>

struct _example
{
	_example(std::string s, std::vector<int> v, int _a, std::unordered_map<int, int> m)
		:str(s), vec(v), a(_a), mp(m)
	{
	}

	std::string str;
	std::vector<int> vec;
	int a;
	std::unordered_map<int, int> mp;
};

int main()
{
	int a { 10 };
	std::string str { "nihao" };
	std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7, 8 };
	std::pair<int, int> p{1, 1};
	std::unordered_map<int, int> mp { p };
	_example exa { str, vec, a, mp };
	std::cout << exa.str << std::endl;
	for (int i = 0; i < exa.vec.size(); i++)
	{
		std::cout << exa.vec[i] << " ";
	}
	std::cout << std::endl;
	std::cout << exa.a << std::endl;
	auto it = exa.mp.begin();
	if (it != exa.mp.end())
	{
		std::cout << it->first << ":" << it->second << std::endl;
	}
	return 0;
}

auto和decltype

auto和decltype都能自动推导变量的类型。
但是auto只能通过其他变量进行初始化, 不能单独使用, 如下:

#include <iostream>
#include <string>
int main()
{
	int a = 0;
	auto b = a;
	std::string str = "nihao";
	auto str2 = str;
	std::cout << b << " : " << str2 << std::endl;
	return 0;
}

decltype能够接收一个变量, 推导出这个变量的类型。更加灵活:

#include <iostream>
#include <string>
int main()
{
	int a = 10;
	decltype(a) b = a;
	decltype(b) c;
	c = a;
	std::cout << b << " " << c << std::endl;
	return 0;
}

范围for

范围for就是遍历一次容器或者数组。 如果是容器或者自定义类型, 那么要封装迭代器, 要有begin和end接口。 那么就能使用范围for。 如果是数组, 那么数组本身就是指针, 所以本身就可以使用范围for。

#include <iostream>
int main()
{
	std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7, 8 };
	for (auto e : vec)
	{
		std::cout << e << std::endl;
	}
	return 0;
}

右值引用

右值引用的符号为&&:

int &&a = int(1); //右值引用

我们平时说的引用其实都是左值引用, 就像下面的示例:
int a = 0;
int &b = a; //这就是一个左值引用。
之所以叫做左值引用是因为他引用的是一个左值。 所以右值引用引用的就是右值。 什么是左值, 什么是右值?

左值和右值

左值和右值的本质区别是能否被取地址能被取地址的值就是左值。不能被取地址的值就叫做右值。 平时写的变量都是左值,因为它们可以被取地址。像临时变量就是右值,字面常量也是右值, 因为它们都不可以被取地址。
对于右值来说, 自定义类型的右值也叫做将亡值

左值引用和右值引用的相同点和不同点

相同点

说完左值右值后我们来谈一谈引用。 不管是左值引用还是右值引用, 博主总结他们都有两个特点:

	引用可以延长临时变量的生命周期。
	引用本身是左值。

下面为示例:

struct exam
{
	exam(int e = 0)
    :_e(e)
	{}
	int _e;
};  
 		
#include <iostream>
int main()
{
    const int &a = int(4);
	const exam &e = exam(10);

	std::cout << a << ":" << e._e << std::endl; //本该在创建后就销毁的数据被延长了。

	int &&a = int(1);  //a右值引用了int(1), 让右值延长生命周期
	auto pa = &a;   //a引用右值后,变得可以被取地址了, 说明成为了左值。
	std::cout << pa << std::endl;
    return 0;
}

不同点

左值引用既可以引用左值也可以引用右值:

#include <iostream>
int main()
{
	int a = 10;
	int &b = a;  //引用左值
	
	const int &c = 1;      //引用右值, 因为右值不可修改, 所以加上const防止权限放大
	const int &d = int(1); //引用右值, 因为临时变量不可修改, 所以加上const防止权限放大
	return 0;
}

右值引用只能引用右值

#include <iostream>
int main()
{
	int &&a = 1;
	int &&b = int(1);
	//int &&c = a;   //语法错误, 因为a是一个左值。
	return 0;
}

move

move能够将一个左值临时转化为左值。 作用域为这一行。

#include <iostream>
int main()
{
	int b = 0;
	int &&a = move(b);   //不会报错, 说明b此时变成了右值
	
	return 0;
}

右值引用的作用

右值引用的出现是为了解决深拷贝开销大的问题。 比如下面这种场景:

#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> Test1()
{
	vector<vector<int>> vv = {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}};
	return vv;
}
void Test2(vector<vector<int>> &vv)
{
	vv.push_back({1, 2, 3});
	vv.push_back({2, 3, 4});
	vv.push_back({3, 4, 5});
}
int main()
{
	vector<vector<int>> vv1 = Test1();
	vector<vector<int>> vv2;
	Test2(vv2);
	return 0;
}

这种场景描述起来就是:函数返回, 函数内部局部变量销毁, 无法返回引用。所以这种情况一般有两种处理方法, 一种就是直接返回临时对象, 如Test1。 还有一种就是输出型参数, 如同Test2。
对于vector<vector<int>> 来说, Test2还没有问题。 但是如果使用Test1,那么开销就大了。 因为对于这个函数返回的过程如下:
在这里插入图片描述
对于Test1(), 它返回的时候是先拷贝出一份**vector<vector<int>>**临时变量。 进行深拷贝, 拷贝出一份资源, 然后vv释放。 之后这个临时变量作为返回值初始化vv1。 初始化vv1的时候, 要调用vv1的拷贝构造。 同样是深拷贝, 那么就还要拷贝出一份临时资源, 拷贝完成之后vv1释放。 这个过程太繁琐了, 因为管理的资源被拷贝了两次, 大佬们就想, 这个被管理的资源, 其实不用被释放,可不可以直接把他转移给vv1呢? 所以就有了移动语义, 所以在c++11种, 容器中都添加了移动构造、移动赋值、移动插入。
下面看一下什么是移动语义

移动语义

移动语义:将一个将亡值管理的资源移动到另一个变量里面

当我们使用一个将亡值构造对象的时候,如果没有移动构造, 就会采用拷贝构造, 那么就会采用深拷贝。 如果有移动构造, 那么就去调用移动构造了。 下面是一个移动构造的例子:

#include <iostream>
using namespace std;
struct my_memory
{
	my_memory(int a = 10)
		:_size(a), _memory(new int(_size))
	{
	}
	my_memory(const my_memory&a)
	{
		cout << "拷贝构造(const my_mymory&a) "<< endl;
		_size = a._size;
		for (int i = 0; i < _size; i++) _memory[i] = a._memory[i];
	}
	my_memory(const my_memory &&a)
	{
		cout << "移动构造(const my_mymory&&a)" << endl;
		_size = a._size;
		_memory = a._memory;  //转移资源
	}
	int _size;
	int *_memory;
}; 

int main()
{
	my_memory m1;
	cout << m1._memory << endl;
	my_memory m2 = move(m1);
	cout << m1._memory << endl;
	return 0;
}

在这里插入图片描述
运行结果会发现, Test1创建的局部变量内部管理的资源成功转移给了m1, 完成了移动构造。
除了移动构造对于移动拷贝和移动插入类似, 都是右值引用接收将亡值。 然后将将亡值的资源转走。如下图为移动赋值:

#include <iostream>
using namespace std;
struct my_memory
{
	my_memory(int a = 10)
		:_size(a), _memory(new int(_size))
	{
	}
	my_memory(const my_memory&a)
	{
		cout << "拷贝构造(const my_mymory&a) "<< endl;
		_size = a._size;
		for (int i = 0; i < _size; i++) _memory[i] = a._memory[i];
	}
	my_memory(const my_memory &&a)
	{
		cout << "移动构造(const my_mymory&&a)" << endl;
		_size = a._size;
		_memory = a._memory;  //转移资源
	}
	my_memory& operator=(my_memory &&a)
	{
    	cout << "移动赋值" << endl;
    	int *tmp = _memory;
   		_memory = a._memory;
    	a._memory = tmp;
    	return *this;
	}

	int _size;
	int *_memory;
}; 

int main()
{
	my_memory m1;
	cout << m1._memory << endl;
	my_memory m2;
	m2 = move(m1);
    cout << m2._memory << endl;
	return 0;
}

在这里插入图片描述

万能引用

c++11中新增了一个叫做万能引用的语法, 就是一个函数内部的参数可以接收左值, 也可以接收右值。 定义方式就是:

template<class T>
返回值 func_name( T &&t)
{
	//函数体
}

在这里插入图片描述

final和override

final

final是一个关键字。
final可以用来修饰虚函数, 表示一个虚函数不能被重写。 即:最后一个虚函数。
在这里插入图片描述
final也可以用来修饰类, 表示这个类不能被继承。 即:最后一个类。
在这里插入图片描述

override

override同样是一个关键字, 用来修饰虚函数。被override修饰的虚函数必须是继承自基类的正在重写的虚函数, 否则语法出错。 所以override就是用来检查, 检查一个虚函数是否是继承自基类的
在这里插入图片描述

可变参数模板

概念

可变参数模板让模板的参数可以是任意的。我们平时说的可变参数包有两种:

  • 模板的可变参数包
  • 函数的可变参数包

可变参数包的定义都是三个点:“”。

  1. 模板的可变参数包class…或者typename…后定义参数包类型名称。
  • 如下为示例代码:

      template<class...  Args>
      void func1(){}
      template<typename... Args>
      void func2(){}
    
  1. 函数的可变参数包是函数的参数是参数包类型, 这个时候这个参数包就是函数的可变参数包。参数包的名称… 表示这是一个参数包的类型, 后面就是参数名。
  • 如下为示例代码:

      template<class... Args>
      void func1(const Args&... args)
      {
      	cout << "nihao" << endl;
      }
    

想要使用模板的可变参数包,我们得先知道可变参数包有什么用。

  • 当我们既不确定要处理的实参的数目, 也不知道要处理的类型有哪些的时候,就可以使用可变参数包。

sizeof

可变参数包的个数可以使用sizeof计算出来, sizeof计算参数包不会计算参数的大小, 而是会计算参数包内参数的个数。

  • 下面为示例:

      template <typename... Args>
      void func2(const Args &...args)
      {
      	cout << sizeof...(args) << endl;
      }
    
      int main()
      {
      	func2(1, 2, 1.1, "nihao", string("nihao"));
    
      	return 0;
      }
    

在这里插入图片描述

参数包的扩展

我们使用参数包, 就要将参数包扩展。 参数包扩展后会隐式的得到一个参数列表:

template <class T, class... Args>	
void print(const T &t, const Args &...args) // Args... 这表示一个扩展。 参数包...。args扩展成一个参数列表
{
	cout << t << endl;

	print(args...);     //参数包..., 表示扩展。 args扩展成一个参数列表
}
  • 下面为调用示例:

    void print(){}
    
    template <class T, class... Args>
    void print(const T &t, const Args &...args) // Args... 这表示一个扩展。 参数包...
    {
    	cout << t << endl;
    
    	print(args...);     //参数包..., 表示扩展
    }
    
    int main()
    {
    	print(1, 2, "hello", string("world"));
    	return 0;
    }
    
  • 上面的第一个print用来结束递归。 因为当下面的第二个print解析参数包到最后一层, 参数列表里面已经没有参数了, 就应该调用一个无参的print, 但是第二个print最少有一个参数cosnt T&, 所以要重载一个无参的print结束递归。

  • 第一次扩展参数包是扩展出了四个参数, print里面一共有着四个参数。 打印第一个参数

    void print(const int &t, const int& int, const char*, const string &)
    {
    	cout << t << endl;
    	print(args...); //将剩下的三个参数传进去
    }
    
  • 第二次扩展参数包, 是扩展出了三个参数,print里面一共有三个参数, 打印第一个参数

    void print(const  int&t, const char*, const string &)
    {
    	cout << t << endl;
    	print(args...); //将剩下的两个参数传进去
    }
    
  • 第三次扩展参数包, 是扩展出了两个参数, print里面一共有两个参数, 打印第一个参数

      void print(const char* t, const string &)
      {
      	cout << t << endl;
      	print(args...); //将剩下的一个参数传进去
      }
    
  • 第四次扩展参数包,是扩展出了一个参数, print里面只有一个参数

    void print(const string &)
    {
    	cout << t << endl;
    	print();   //无参,调用无参print。 
    }
    

理解包扩展

上面只是对参数包单纯进行扩展。其实参数包还可以作为参数传递给函数。意思就是希望参数包中的每个参数都去调用函数

  • 下图为示例:

    void print(){}
    template <class T, class... Args>
    void print(const T &t, const Args &...args) // Args... 这表示一个扩展。 参数包...
    {
    	cout << t << endl;
    
    	print(args...);     //参数包..., 表示扩展
    }
    
    int double_num(int x)
    {
    	return 2 * x;
    }
    
    template<class... Args>
    void print_double(const Args&... args)
    {
    	print(double_num(args)...);  //将每个参数去调用double_num函数。
    }
    
    int main()
    {
    	print_double(1, 2, 3, 4, 5);
    	return 0;
    } 
    

完美转发

forward完美转发是一个函数, 可以用来保持参数的特性。在c++11中可以使用forward配合参数包编写函数。我们利用标准模板库容器(stl容器) 里面的emplace_back来看一下forward完美转发怎么配合参数包的。

  • 假如下面是vector类:

      class my_vector
      {
      	template<class... Args>
      	void emplace_back(const Args&&... args);
      };
    
  • 标准模板库容器里面的emplace_back是一个有可变参数模板的函数, 它本质上是一个插入函数, 但是插入数据时对节点的拷贝操作由调用拷贝构造深拷贝变成了调用移动构造进行资源转移

    class my_vector
    {
    	template<class... Args>
    	void emplace_back(const Args&&... args)
    	{
    		insert(forward<Args>(args)...);  //假设这里的insert是vector的构造节点操作。 又因为这里本质上传过去的是右值, 所以构造节点时会调用参数包参数类型的移动构造去构造节点。
    	}
    }
    
  • 假如这里的参数包里面的右值类型是string类型,右值为"hello world", 那么就相当于下面:

      forward<string>(string("hello world"));
    

最后这里就是调用了string类型的移动构造, 省去了string类型的深拷贝。

lambda表达式

  • lambda表达式是一中可调用对象。和回调函数, 仿函数类似。
  • lambda表达式最终会被编译器转化为匿名仿函数对象

lambda的基本语法

	[capature](params) mutable(可以不加, 默认情况下不加)-->  return_type {body}
语法作用
[ ]捕捉作用域内的变量
()参数列表
mutable可以不加,将仿函数改为非const调用
return_type返回值,不写可以自动推导
body函数体

捕获方式

  • 捕捉作用域内的变量的方式有很多, 下面是一些捕获方式
捕获符号作用
[&]引用捕捉, 所有变量引用进入lambda
[=]值捕捉, 所有变量的值拷贝进入lambda
[&变量名]引用捕捉某个变量
[变量名]值捕捉某个变量
[ ]不捕捉任何变量
[=, &x]默认值捕获,但 x 是引用捕获
thisthis指针捕获
  • 捕捉到lambda里面的值,其实就等价于一个类里面的成员变量。 如果是引用捕捉, 就相当于在类里面创建了一个引用变量进行引用值捕捉就相当于创建了一个普通变量进行拷贝

mutable

如果匿名仿函数对象是一个右值, 那么就说明这个匿名仿函数的this指针式const类型的, 所以它只能调用const成员方法。 也就是说lambda表达式其实本质上就相当于一个仿函数类里面的const修饰的operator()方法

  • 如果修改捕捉到的值,不能直接修改。
  • 加上mutable,可以修改

代码演示

int main()
{
	int a = 0;

	auto f1 = [a](int x)
	{
		cout << x << endl;
	};

	auto f2 = [a] (int x) mutable 
	{
		a = x;
		cout << a << endl;
	};

	f1(10);
	f2(10);

	return 0;
}

在这里插入图片描述

包装器

c++11中引入了包装器库 functional。 关于包装器的内容博主之前讲过:
https://blog.csdn.net/strive_mianyang/article/details/139565882?spm=1001.2014.3001.5501

在这里插入图片描述

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

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

相关文章

LeetCode 1123.最深叶节点的最近公共祖先 题解

昨天写了三题&#xff0c;今天目前为止写了一题&#xff0c;玩了会游戏稍微休息了下 先来理解一下今天的题目&#xff0c;今天的题目类型是递归遍历找最深位置&#xff0c;先通过几个样例了解一下 输入&#xff1a;root [3,5,1,6,2,0,8,null,null,7,4] 输出&#xff1a;[2,7,…

工具介绍 | SafeLLMDeploy教程来了 保护本地LLM安全部署

SafeLLMDeploy&#xff1a;保护本地大语言模型安全部署的“守护者” 在AI技术飞速发展的今天&#xff0c;大语言模型&#xff08;LLM&#xff09;如GPT、DeepSeek等正以前所未有的方式改变着我们的工作和生活。然而&#xff0c;本地部署这些强大的AI系统在带来便利的同时&…

机器学习-04-分类算法-03KNN算法案例

实验名称 K近邻算法实现葡萄酒分类 实验目的 通过未知品种的拥有13种成分的葡萄酒&#xff0c;应用KNN分类算法&#xff0c;完成葡萄酒分类&#xff1b; 熟悉K近邻算法应用的一般过程&#xff1b; 通过合理选择K值从而提高分类得到正确率&#xff1b; 实验背景 本例实验…

Java 搭建 MC 1.18.2 Forge 开发环境

推荐使用 IDEA 插件 Minecraft Development 进行创建项目 创建完成后即可进行 MOD 开发。 但是关于 1.18.2 的开发教程太少&#xff0c;因此自己研究了一套写法&#xff0c;写法并非是最优的但是是探索开发MOD中的一次笔记和记录 GITHUB: https://github.com/zimoyin/zhenfa…

计算机网络知识点汇总与复习——(三)数据链路层

Preface 计算机网络是考研408基础综合中的一门课程&#xff0c;它的重要性不言而喻。然而&#xff0c;计算机网络的知识体系庞大且复杂&#xff0c;各类概念、协议和技术相互关联&#xff0c;让人在学习时容易迷失方向。在进行复习时&#xff0c;面对庞杂的的知识点&#xff0c…

主机和虚拟机间的网络通信

参考&#xff1a;Vmware虚拟机三种网络模式详解 - 林加欣 - 博客园 (cnblogs.com) 虚拟机配置 一般额外配置有线和无线网络 桥接模式 虚拟机和主机之间是同一个网络&#xff0c;用一根线连接了虚拟机和物理机的网卡,可以选择桥接的位置&#xff0c;默认情况下是自动桥接&…

嵌入式Linux开发环境搭建,三种方式:虚拟机、物理机、WSL

目录 总结写前面一、Linux虚拟机1 安装VMware、ubuntu18.042 换源3 改中文4 中文输入法5 永不息屏6 设置 root 密码7 安装 terminator8 安装 htop&#xff08;升级版top&#xff09;9 安装 Vim10 静态IP-虚拟机ubuntu11 安装 ssh12 安装 MobaXterm &#xff08;SSH&#xff09;…

说清楚单元测试

在团队中推行单元测试的时候,总是会被成员问一些问题: 这种测试无法测试数据库的SQL(或者是ORM)是否执行正确?这种测试好像没什么作用?关联的对象要怎么处理呢?…借由本篇,来全面看一看单元测试。 单元测试是软件开发中一种重要的测试方法,其核心目的是验证代码的最小…

电商---part02 项目环境准备

1.虚拟机环境 可以通过VMWare来安装&#xff0c;但是通过VMWare安装大家经常会碰到网络ip连接问题&#xff0c;为了减少额外的环境因素影响&#xff0c;Docker内容会通过VirtualBox结合Vagrant来安装虚拟机。 VirtualBox官网&#xff1a;https://www.virtualbox.org/ Vagran…

LabVIEW提升程序响应速度

LabVIEW 程序在不同计算机上的响应速度可能存在较大差异&#xff0c;这通常由两方面因素决定&#xff1a;计算机硬件性能和程序本身的优化程度。本文将分别从硬件配置对程序运行的影响以及代码优化方法进行详细分析&#xff0c;帮助提升 LabVIEW 程序的执行效率。 一、计算机硬…

工业领域网络安全技术发展路径洞察报告发布 | FreeBuf咨询

工业网络安全已成为国家安全、经济稳定和社会运行的重要基石。随着工业互联网、智能制造和关键基础设施的数字化升级&#xff0c;工业系统的复杂性和互联性显著提升&#xff0c;针对工业领域的网络攻击朝着目标多样化、勒索攻击产业化、攻击技术持续升级的方向发展&#xff0c;…

WPF 登录页面

效果 项目结构 LoginWindow.xaml <Window x:Class"PrismWpfApp.Views.LoginWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.…

【数学建模】动态规划算法(Dynamic Programming,简称DP)详解与应用

动态规划算法详解与应用 文章目录 动态规划算法详解与应用引言动态规划的基本概念动态规划的设计步骤经典动态规划问题1. 斐波那契数列2. 背包问题3. 最长公共子序列(LCS) 动态规划的优化技巧动态规划的应用领域总结 引言 动态规划(Dynamic Programming&#xff0c;简称DP)是一…

leetcode-代码随想录-链表-移除链表元素

题目 链接&#xff1a;203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 …

论文阅读Diffusion Autoencoders: Toward a Meaningful and Decodable Representation

原文框架图&#xff1a; 官方代码&#xff1a; https://github.com/phizaz/diffae/blob/master/interpolate.ipynb 主要想记录一下模型的推理过程 &#xff1a; %load_ext autoreload %autoreload 2 from templates import * device cuda:1 conf ffhq256_autoenc() # pri…

Python集合(五)

集合一&#xff1a; 跟字典一样&#xff0c;最大的特性就是唯一性&#xff0c;集合中的所有的元素都是独一无二的&#xff0c;并且还是无序的 创建集合 第一种&#xff1a; 第二种&#xff1a;集合推导式&#xff1a; 第三种&#xff1a;使用类型构造器&#xff1a; 集合是无…

ISIS多区域配置

一、什么是ISIS多区域 ISIS&#xff08;Intermediate System to Intermediate System&#xff09;多区域是指网络被划分为多个逻辑区域&#xff08;Areas&#xff09;&#xff0c;不同区域之间通过特定的ISIS路由器&#xff08;Level-1-2&#xff09;进行路由交互。多区域设计提…

2025-04-04 Unity 网络基础5——TCP分包与黏包

文章目录 1 分包与黏包2 解决方案2.1 数据接口2.2 定义消息2.3 NetManager2.4 分包、黏包处理 3 测试3.1 服务端3.2 客户端3.3 直接发送3.4 黏包发送3.5 分包发送3.6 分包、黏包发送3.7 其他 1 分包与黏包 ​ 分包、黏包指在网络通信中由于各种因素&#xff08;网络环境、API …

chromium魔改——绕过无限debugger反调试

在进行以下操作之前&#xff0c;请确保已完成之前文章中提到的 源码拉取及编译 部分。 如果已顺利完成相关配置&#xff0c;即可继续执行后续操作。 在浏览器中实现“无限 debugger”的反调试技术是一种常见的手段&#xff0c;用于防止他人通过开发者工具对网页进行调试或逆向…

JS dom修改元素的style样式属性

1通过样式属性修改 第三种 toggle有就删除 没就加上