C++ 11 异常

news2024/10/7 4:31:26

  在C语言中,我们也有不少处理错误的方式,但是我们将这些处理错误的方式带到C++ 中,随着C++不断更新的语法规则和内容下,这些C语言的处理方式还够用吗?


一.C语言的错误处理方式

C语言处理错误的方式大概有两种:


1.1 终止程序

  一般使用形式如  assert 函数。

  但这种使用方式也有很多缺点,首先这个直接停止掉程序就很让大家难以接受。

  再比如说在代码的运行过程遇到了严重错误比如说:表达式中的除0错误,数组的越界访问错误和野指针带来的错误,都会导致程序在运行的过程中出现错误从而终止了程序。

 这种处理方式显然是很奇怪的,实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。

2.2 错误码返回

  错误码属于返回码的一种,像我们运行成功的程序。

  

  返回错误码,一般错误的程序就会返回一个错误码,那错误码就会存在一个缺点:需要程序员自己去查找对应的错误,这点就极其麻烦。如系统的很多库的接口函数都是通过把错误码放到errno中表示错误,那么这就是c语言的第二种报错方式通过错误码来查找错误。

 二.C++ 异常

  为了让程序员更好的查找错误,和应对C++ 新引入的各种特性和规则,C++ 引入了异常这一个机制。

2.1  C++ 异常的概念

  异常实际上就是一个C++ 新引入的处理程序错误的一个新语法规则,其本质上由三个关键字组成:

  try:本质上是一片区域,称之为块,我们把可能会出现错误的代码放到这个块里面,后面常跟着catch块。

 throw:当问题出现时,我们需要抛出这个问题,抛出这个行动就是由throw来完成的。

 catch:来捕获throw抛出的异常对象,并对这些对象进行分析,catch可能有多个。

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。

使用 try/catch 语句的语法如下所示:

try
{
	// 保护的标识代码
}
catch (ExceptionName e1)
{
	// catch 块
}
catch (ExceptionName e2)
{
	// catch 块
}
catch (ExceptionName eN)
{
	// catch 块
}
//catch...

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

三 异常的使用

3.1 异常的抛出和捕获

  1. 异常是通过 throw 抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
  2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回
  4. catch(...) 可以捕获任意类型的异常,问题是不知道异常错误是什么,一般我们会在里面提醒抛出了个任意异常。
  5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用,我们后面会详细讲解这个。

下面对这五点进行测试:

3.2 第一规则测试

(1)对于第一点: 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个 catch 的处理代码

测试代码,抛异常时:在下面抛出的对象是一个字符串,它储存在常量区,对应的捕获 catch 的类型就是 const char*,不能是 char*,如果是 char* 会出现权限放大
 

#include<iostream>
using namespace std;


double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw "Division by zero condition!";
	else
		return ((double)a / (double)b);
}
void Func()
{
	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;
}
int main()
{
	try
	{
		// try 块中放置可能抛出异常的代码
		Func();
	}
	catch (const char* errmsg)// catch 关键字用于捕获异常
	{
		cout << errmsg << endl;
	}

	return 0;
}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

这里我们挨个输入,测试一下:


1.输入10 5,不引发异常,代码正常运行:

2. 输入 10 0 发生除0错误,引发异常,catch 选项类型匹配则执行 catch 里面的代码,打印错误信息,程序正常结束;否则不执行,直接终止程序 。

  

3. 修改 const char* errmsg 为  char* errmsg,运行,输入 10 0,发生除0错误,抛出异常,catch 选项类型不匹配,捕获失败,程序终止 。

 3.3 第二规则测试

    被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。

  请看以下代码:
    

#include<iostream>
using namespace std;


double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw "Division by zero condition!";
	else
		return ((double)a / (double)b);
}
void Func()
{
	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;
}
int main()
{
	try
	{
		// try 块中放置可能抛出异常的代码
		try
		{
			Func();
		}
		catch (const char* errmsg)//类型匹配,就近原则
		{
			cout << "距离为1" << endl;
			cout << errmsg << endl;
		}
	}
	catch (const char* errmsg)//类型匹配
	{
		cout << "距离为2" << endl;
		cout << errmsg << endl;
	}

	return 0;
}

显示由距离近的捕获: 

 3.4 第四规则测试

   第三规则比较麻烦,我这里就不展开测试了,有兴趣的可以自己测试一下。

#include<iostream>
using namespace std;


double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw "Division by zero condition!";
	else
		return ((double)a / (double)b);
}

void Func()
{
	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;
}

int main()
{
	try
	{
		// try 块中放置可能抛出异常的代码
		Func();
	}
	//类型不匹配,
	catch ( char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}

	return 0;
}

可以看出 catch(...)匹配了抛出的异常。

第五条规则我们下面详细展开讲解。 

3.5 在函数调用链中异常栈展开匹配原则

  1. 首先检查 throw 本身是否在 try块内部,如果是再查找匹配的 catch 语句。如果有匹配的,则调到 catch 的地方进行处理。
  2. 没有匹配的 catch 则退出当前函数栈,继续在调用函数的栈中进行查找匹配的 catch。
  3. 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个 catch(...) 捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
  4. 找到匹配的 catch 子句并处理以后,会继续沿着 catch 子句后面继续执行。

  比如下面的代码中main函数中调用了func3, func3中调用了func2,func2中调用了func1,在 func1 中抛出了一个 string 类型的异常对象。

  

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

void func1()
{
	throw string("这是一个异常");
}
void func2()
{
	func1();
}
void func3()
{
	func2();
}


int main()
{
	try
	{
		func3();
	}
	catch (const string& s)
	{
		cout << "错误描述:" << s << endl;
	}
	catch (...)
	{
		cout << "Unkown Exception:未知异常" << endl;
	}
	return 0;
}

当func1中的异常被抛出后:

  • 首先会检查throw本身是否在 try 块内部,这里由于 throw 不在 try 块内部,因此会退出 func1 所在的函数栈,继续在上一个调用函数栈中进行查找,即 func2 所在的函数栈。
  • 由于 func2 中也没有匹配的 catch,因此会继续在上一个调用函数栈中进行查找,即func3所在的函数栈。
  • func3 中也没有匹配的 catch,于是就会在 main 所在的函数栈中进行查找,最终在 main 函数栈中找到了匹配的 catch。
  • 这时就会跳到 main 函数中对应的 catch 块中执行对应的代码块,执行完后继续执行该代码块后续的代码
     

 上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。但在实际使用中我们最后都要加一个 catch(...)  捕获任意类型的异常,否则当有异常没捕获时,程序就会直接终止。

3.6 异常的重新抛出

  有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理.

  如下面这段代码:
 

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

void func1()
{
	try
	{
		throw string("除0异常");
	}
	catch (const string& s)
	{
		//打印提示信息,简单处理
		cout << s << endl;
		// 重新抛出,让外层处理错误
		throw;
	}
}
int main()
{
	try
	{
		func1();
	}
	catch (const string& s)
	{
		cout << "进行处理..." << s << endl;
	}
	catch (...)
	{
		cout << "Unkown Exception:未知异常" << endl;
	}
	return 0;
}

运行结果,异常抛给了外层,外层进行处理 

直接让外层捕获异常进行处理可能会引发一些问题。比如: 

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

void func1()
{
	//...
	throw string("除0异常");
}

void func2()
{
	int* array = new int[10];
	func1();

	//将delete函数放在throw之后
	delete[] array;
	cout << "内存已释放" << endl;
}
int main()
{
	try
	{
		func2();
	}
	catch (const string& s)
	{
		cout << s << ", 进行处理..." << endl;
	}
	catch (...)
	{
		cout << "Unkown Exception:未知异常" << endl;
	}
	return 0;
}

  func2中通过 new操作符申请了一块内存空间,并且在 func2 最后通过 delete 对该空间进行了释放,但由于 func2 中途调用的 func1 内部抛出了一个异常,这时会直接跳转到main函数中的 catch 块执行对应的异常处理程序,这时就导致func2中申请的内存块没有得到释放,造成了内存泄露

运行结果,内存没有释放

这时可以在 func2 中先对 func1 抛出的异常进行捕获,捕获后先将申请到的内存释放再将异常重新抛出,这时就避免了内存泄露

修改代码

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

void func1()
{
	//...
	throw string("除0异常");
}

void func2()
{
	cout << "func2" << endl;
	int* array = new int[10];
	try
	{
		func1();
	}
	catch (...)
	{
		cout << "delete []" << array << endl;
		delete[] array;
		throw;
	}
	//...
	delete[] array;
	cout << "array内存已释放" << endl;
}
int main()
{
	try
	{
		func1();
	}
	catch (const string& s)
	{
		cout << s << ", 进行处理..." << endl;
	}
	catch (...)
	{
		cout << "Unkown Exception:未知异常" << endl;
	}
	return 0;
}

 内存释放完成:

四.异常安全 

  将抛异常导致的安全问题叫做异常安全问题,对于异常安全问题下面给出几点建议:

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化。
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)。
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在 lock 和 unlock 之间抛出了异常导致死锁,C++经常使用 RAII 来解决以上问题,关于 RAII,智能指针这节进行讲解。

五.异常规范 

  异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。

5.1 C++ 98中的异常规范

  这是 C++98 的做法: 

  • 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型
  • 函数的后面接throw(),表示函数不抛异常
  • 若无异常接口声明,则此函数可以抛掷任何类型的异常
  • 但是这些规范写法复杂,而且这些规范没有强制,最后形同虚设

比如: 

#include<iostream>

using namespace std;
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();

实际上,形同虚设,如:

#include<iostream>

using namespace std;

// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(int)
{
	throw(3.3);
}
// 这里表示这个函数不会抛出异常
void fun2() throw()
{
	throw(3.3);
}


int main()
{
	try {
		fun();
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}

	try {
		fun2();
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}
	return 0;
}

看结果:

证明98中的规范确实没什么作用。

5.2 C++ 11中的异常规范

C++11 的做法

  • C++11 中新增的 noexcept,表示不会抛异常
  • 这是对 C++98 的异常规范复杂写法的简化

 例如:

#include<iostream>
using namespace std;

//这里表示这个函数不会抛出异常
void fun2()  noexcept
{
	throw(3.3);
}

int main()
{
	try
	{
		fun2();
	}
	catch (const std::exception&)
	{

	}
	return 0;
}

直接报错:

六.自定义异常体系 

  实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,进行异常模块划分,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。

最基础的异常类至少需要包含错误编号和错误描述两个成员变量

比如,有以下异常基类:


#include<iostream>
#include<string>

using namespace std;
// 服务器开发中通常使用的异常继承体系
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}
	virtual string what() const
	{
		return _errmsg;
	}
protected:
	string _errmsg;
	int _id;
};

  其他模块如果要对这个异常类进行扩展,必须继承这个基础的异常类,可以在继承后的异常类中按需添加某些成员变量,或是对继承下来的虚函数what进行重写,使其能告知程序员更多的异常信息 

 例如:
 

//以下三个派生类都是异常基类的派生类,表示某种错误
class SqlException : public Exception
{
public:
	SqlException(const string& errmsg, int id, const string& sql)
		:Exception(errmsg, id)
		, _sql(sql)
	{}
	virtual string what() const
	{
		string str = "SqlException:";
		str += _errmsg;
		str += "->";
		str += _sql;
		return str;
	}
private:
	const string _sql;
};


class CacheException : public Exception
{
public:
	CacheException(const string& errmsg, int id)
		:Exception(errmsg, id)
	{}
	virtual string what() const
	{
		string str = "CacheException:";
		str += _errmsg;
		return str;
	}
};
class HttpServerException : public Exception
{
public:
	HttpServerException(const string& errmsg, int id, const string& type)
		:Exception(errmsg, id)
		, _type(type)
	{}
	virtual string what() const
	{
		string str = "HttpServerException:";
		str += _type;
		str += ":";
		str += _errmsg;
		return str;
	}
private:
	const string _type;
};


//以下三个函数是判断错误函数,如果错误,就抛出异常派生类,但这里我们只是模拟一下
void SQLMgr()
{
	srand(time(0));
	if (rand() % 7 == 0)
	{
		throw SqlException("权限不足", 100, "select * from name = '张三'");
	}
	//throw "xxxxxx";
}
void CacheMgr()//权限报错
{
	srand(time(0));
	if (rand() % 5 == 0)
	{
		throw CacheException("权限不足", 100);
	}
	else if (rand() % 6 == 0)
	{
		throw CacheException("数据不存在", 101);
	}
	SQLMgr();
}
void HttpServer()// http报错
{
	// ...
	srand(time(0));
	if (rand() % 3 == 0)
	{
		throw HttpServerException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpServerException("权限不足", 101, "post");
	}
	CacheMgr();
}

开始测试:
 


#include <windows.h>
int main()
{
	while (1)
	{
		Sleep(1000);
		try {
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}
	return 0;
}

运行结果:

七.C++标准库的异常体系

  C++ 提供了一系列标准的异常,定义在 std::exception 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示

 

 说明:实际中我们可以可以去继承exception类实现自己的异常类。但是实际中很多公司像上面一
样自己定义一套异常继承体系。因为C++标准库设计的不够好用。

八.异常的优缺点

C++异常的优点:

  1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。
  2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们得层层返回错误,最外层才能拿到错误,具体看下面的详细解释。
  3. 很多的第三方库都包含异常,比如 boost、gtest、gmock 等等常用的库,那么我们使用它们也需要使用异常。
  4. 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如 T& operator 这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。

C++异常的缺点:

 

  1. 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难。
  2. 异常会有一些性能的开销。当然在现代硬件速度很快的情况下,这个影响基本忽略不计。
  3. C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题。学习成本较高。
  4. C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
  5. 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用 fun()noexcept 的方式规范化。
  6. 总结:异常总体而言,利大于弊,所以工程中我们还是鼓励使用异常的。另外 OO 的语言基本都是用异常处理错误,这也可以看出这是大势所趋。
     

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

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

相关文章

环境安全之配置管理及配置安全设置指导

一、前言 IT运维过程中&#xff0c;配置的变更和管理是一件非常重要且必要的事&#xff0c;除了一般宏观层面的配置管理&#xff0c;还有应用配置参数的配置优化&#xff0c;本文手机整理常用应用组件配置项配置&#xff0c;尤其安全层面&#xff0c;以提供安全加固指导实践。…

mysqlclient安装失败

错误代码如下: 原因&#xff1a;缺少依赖项 从您所提供的错误日志中可以看出&#xff0c;尝试安装mysqlclient时出现了问题。错误的核心部分是&#xff1a; Can not find valid pkg-config name. Specify MYSQLCLIENT_CFLAGS and MYSQLCLIENT_LDFLAGS env vars manually 这表…

高通平台开发系列讲解(USB篇)MBIM协议详解

文章目录 一、MBIM协议二、MBIM 消息类型三、基本控制消息构成3.1、MBIM OPEN MSG FORMAT3.2、MBIM CLOSE MSG FORMAT3.3、MBIM_COMMAND_MSG3.4、MBIM_COMMAND_DONE3.5、MBIM_INDICATE_STATUS_MSG四、MBIM Message(UUID+CID)4.1、UUID_BASIC_CONNECT

redis的深度理解

上篇博客我们说到了redis的基本概念和基本操作&#xff0c;本篇我们就更深入去了解一些redis的操作和概念&#xff0c;我们就从red的主从同步、redis哨兵模式和redis集群三个方面来了解redis数据库 一、主从同步 像MySQL一样&#xff0c;redis是支持主从同步的&#xff0c;而…

12月12日作业

设计一个闹钟 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> #include <QTime> #include <QTime> #include <QTextToSpeech>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass …

【深度学习】强化学习(六)基于值函数的学习方法

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略&#xff08;Policy&#xff09;4、马尔可夫决策过程5、强化学习的目标函数6、值函数7、深度强化学习 二、基于值函数的学习方法 一、强化学习问题 强化学习的基本任务是通过智能体与环境的交互学习一个策略…

GeMap:Online Vectorized HD Map Construction using Geometry

参考代码&#xff1a;GeMap 动机与出发点 出了原本针对单点的L1损失&#xff0c;车道线具备的几何结构信息作为监督信息也可以再被挖掘挖掘&#xff0c;像车道线实例中点和点之间的距离与夹角、线与线之间的夹角、不同线上点与点之间的关系都可用来作为监督约束&#xff0c;但…

Redis - RDB与AOF持久化技术

Redis 持久化技术 RDB 是默认持久化方式&#xff0c;但 Redis 允许 RDB 与 AOF 两种持久化技术同时 开启&#xff0c;此时系统会使用 AOF 方式做持久化&#xff0c;即 AOF 持久化技术的优先级要更高。同样的道 理&#xff0c;两种技术同时开启状态下&#xff0c;系…

字符设备驱动开发基础

一. 简介 本文简单了解一下&#xff0c;在字符设备驱动开发开始前对其一些基本认识。简单了解一下&#xff0c;应用程序与驱动的交互原理&#xff0c;以及字符设备驱动开发流程。 二. 字符设备驱动开发流程 1. 在 Linux 中一切皆为文件&#xff0c;驱动加载成功以后会在“…

【教程】制作 iOS 推送证书

​ 目录 证书类型 MAC Key Store 消息推送控制台 制作证书 创建苹果 App ID 使用appuploder制作 .p12文件 创建证书 如需向 iOS 设备推送数据&#xff0c;您首先需要在消息推送控制台上配置 iOS 推送证书。iOS 推送证书用于推送通知&#xff0c;本文将介绍消息推送服务支…

K8S(二)—介绍

K8S的整体结构图 k8s对象 在 Kubernetes 系统中&#xff0c;Kubernetes 对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。 具体而言&#xff0c;它们描述了如下信息&#xff1a; 哪些容器化应用正在运行&#xff08;以及在哪些节点上运行&#xff09;可…

【Java 进阶篇】Jedis 操作 Hash:Redis中的散列类型

在Redis中&#xff0c;Hash是一种存储键值对的数据结构&#xff0c;它适用于存储对象的多个属性。Jedis作为Java开发者与Redis交互的工具&#xff0c;提供了丰富的API来操作Hash类型。本文将深入介绍Jedis如何操作Redis中的Hash类型数据&#xff0c;通过生动的代码示例和详细的…

Github仓库远程操作——简单版

Github远程操作 github仓库简单的远程操作&#xff0c;更多复杂的功能请参考github官方文档 标题 Github远程操作添加公钥到githubGithub仓库远程操作 远程操作之前&#xff0c;先添加本地的公钥到github 添加公钥到github 创建本地ssh公私钥&#xff1a;使用powershell或者gi…

Kubernetes实战(十三)-使用kube-bench检测Kubernetes集群安全

1 概述 在当今云原生应用的开发中&#xff0c;Kubernetes已经成为标准&#xff0c;然而&#xff0c;随着其使用的普及&#xff0c;也带来了安全问题的挑战。本文将介绍如何使用kube-bench工具来评估和增强Kubernetes集群的安全性。 2 CIS (Center for Internet Security)简介…

DNSLog漏洞探测(三)之XSS漏洞实战

DNSLog漏洞探测(三)之XSS漏洞实战 通过前面的学习&#xff0c;我们已经明白了什么是DNSLog平台&#xff0c;那么DNSLog平台到底能为我们做些什么呢&#xff1f; DNSLog的平台实际使用很长见的一种情况就是针对漏洞无回显的情况&#xff0c;我们通过让受害者的服务器主动发起对…

数据结构之----数组、链表、列表

数据结构之----数组、链表、列表 什么是数组&#xff1f; 数组是一种线性数据结构&#xff0c;它将相同类型的元素存储在连续的内存空间中。 我们将元素在数组中的位置称为该元素的索引。 数组常用操作 1. 初始化数组 我们可以根据需求选用数组的两种初始化方式&#xff…

Docker容器:Centos7搭建Docker镜像私服harbor

目录 1、安装docker 1.1、前置条件 1.2、查看当前操作系统的内核版本 1.3、卸载旧版本(可选) 1.4、安装需要的软件包 1.5、设置yum安装源 1.6、查看docker可用版本 1.7、安装docker 1.8、开启docker服务 1.9、安装阿里云镜像加速器 1.10、设置docker开机自启 2、安…

【Hadoop_05】NN、2NN以及DataNode的工作机制

1、NameNode和SecondaryNameNode1.1 NN和2NN工作机制1.2 Fsimage和Edits解析1.3 CheckPoint时间设置 2、DataNode2.1 DataNode工作机制2.2 数据完整性2.3 掉线时限参数设置 1、NameNode和SecondaryNameNode 1.1 NN和2NN工作机制 思考&#xff1a;NameNode中的元数据是存储在哪…

css选择器介绍

css选择器介绍 01 css概念介绍 用于更改标签的视觉效果 02 css格式 选择器 {属性1&#xff1a;值1&#xff1b;属性2&#xff1a;值2} 03 三种样式 1.内联样式 直接写在标签的style属性中。 优点&#xff1a;简单明显缺点&#xff1a;无法重复使用代码 <img src&quo…

【Amis Low Code 结合FastAPI进行前端框架开发】

官方文档 封装思想 直接复制官网json数据即可开发每个json中的接口由fastapi 转发&#xff08;透传&#xff09;使其开发模式与前端思维一致 基础组件 from amis import Page, Service, App from pydantic import BaseModel, Field from fastapi import FastAPI, Request, …