C++『异常』

✨个人主页: 北 海
🎉所属专栏: C++修行之路
🎃操作环境: Visual Studio 2022 版本 17.6.5

成就一亿技术人


文章目录

  • 🌇前言
  • 🏙️正文
    • 1.异常基本概念
      • 1.1.C语言异常处理方式
      • 1.2.C++异常处理方式
    • 2.异常的使用
      • 2.1.异常的抛出与捕获
      • 2.2.异常的重新抛出
      • 2.3.异常安全
      • 2.4.异常规范
    • 3.异常体系
      • 3.1.C++标准库的异常体系
      • 3.2.自定义异常体系
    • 4.异常的优缺点
  • 🌆总结


🌇前言

异常处理在软件开发中扮演着关键的角色,它为程序员提供了一种有力的手段来处理和响应程序执行过程中可能出现的错误。本文将深入探讨异常的基本概念、异常处理方式、异常的使用技巧和异常体系的设计,以帮助开发者更好地理解和应用异常处理机制


🏙️正文

1.异常基本概念

1.1.C语言异常处理方式

C语言 中,面对异常主要有以下两种处理方式:

  1. 返回错误码
  2. 终止进程

比如 main 函数有一个返回值,只有返回值(错误码)为 0 时才表示程序正常退出,如果发生越界访问、堆栈溢出等行为时,会返回其他数值

部分错误码及其对应的错误信息对照表格如下

代码错误信息
0成功(Success)
1一般错误(General error)
2误用shell命令(Misuse of shell command)
126无法执行(Cannot execute)
127命令未找到(Command not found)
128无效的退出参数(Invalid exit argument)
130Ctrl+C终止(Terminated by Ctrl+C)
255退出状态未知(Unknown exit status)

至于其他代码的具体含义,取决于编译器的不同的实现,比如上面的 3 号错误码在 VS 中就表示 异常退出,具体原因是 越界访问


除了返回错误码外,C语言 还支持通过函数终止进程,说白了就是给进程发送 信号

可以使用 exit(err_code)abord()assert(bool_exp) 等函数终止进程

exit(err_code) 支持在终止进程时设置错误码,可以根据自己的需要建立 [错误码, 错误信息] 的映射关系

abord() 函数则是直接发送 6 号信号来终止进程

至于 assert(bool_exp) 常用于非法情况的检查判断,bool_exp 是一个返回类为 bool 的表达式,如果该表达式为 ,那么 assert 函数就会触发,并终止进程

注意: 使用 assert 需要包含相关头文件

#include <iostream>
#include <cassert>

using namespace std;

int main()
{
	int x = 10;
	int y = 0;

	// 简易整数除法
	[&]()->void
		{
			// 除数(分母)不能为 0
			assert(y);

			cout << x / y << endl;
		}();

	return 0;
}

assert 最大的优点在于 指明终止原因,以及原因出现的具体路径、具体行号,对于程序调试十分友好,需要 注意 的是 assert 只能在 Debug 模式下使用,Release 模式中 assert 会被自动删除

1.2.C++异常处理方式

无论是 错误码 还是 终止进程,都只能提供简略的错误信息,对于 C++ 这种面向对象的语言来说太无力了,需要一种全新的异常处理方式:将异常化做一个对象,配合异常体系解读异常

万物皆可为对象,所以新的异常处理方式非常强大

C++ 中新增了以下三个关键字,用于实现 异常监测、异常抛出、异常捕获

  • try 监测当前代码区域是否存在异常
  • throw 识别到异常后,抛出异常
  • catch 捕获抛出的异常(如果有的话)

注:throw 是一个关键字,可以直接在后面跟异常对象,也可以像函数调用一样传递异常对象,类似于 sizeof 关键字

比如这样就可以使用 C++ 的异常处理方式

void func()
{
	// 出现异常,抛出
	throw "这是一个异常"
	// 或者
	// throw("这是一个异常")
}

int main()
{
	try
	{
		// 监测代码当前代码区域
		func();
	}
	catch(const char* ps)
	{
		// 捕获异常
		// 可以对 ps 进行操作
	}
	return 0;
}

注意: catch 块捕获的异常对象类型,必须与 throw 抛出的异常对象类型匹配上,否则会导致异常无法捕获,导致程序异常终止

如果正确编写异常处理的代码,try 内的代码发生异常时可以优雅处理,不至于直接引发进程终止,因此 try 内的代码又被称为 保护代码


2.异常的使用

2.1.异常的抛出与捕获

异常的使用比较简单,将之前整数相除的代码改成 C++ 的异常处理方式

void divisor(int x, int y)
{
	if (y == 0)
	{
		// 除 0 错误,抛出异常
		throw("除数(分母)不能为0");
	}

	cout << "结果:" << x / y << endl;
}

int main()
{
	try
	{
		divisor(10, 0);
	}
	catch (const char* s)
	{
		cout << s << endl;
	}

	return 0;
}

通常需要在异常捕获的地方记录日志,方便排查错误

如果传入的数据是正确的,就不会触发异常,程序正常运行

// ...

int main()
{
	try
	{
		divisor(10, 10);
	}
	catch(const char* s)
	{
		cout << s << endl;
	}
	
	return 0;
}

异常在抛出后是必须被捕获的,如果不写 catch 块相关代码或者 catch 块中的类型与抛出的异常类型不匹配,在出现异常后,进程会因异常没有被捕获,而被 abort 函数终止

void divisor(int x, int y)
{
	if (y == 0)
	{
		// 除 0 错误,抛出异常
		throw("除数(分母)不能为0");
	}

	cout << "结果:" << x / y << endl;
}

int main()
{
	try
	{
		divisor(10, 0);
	}
	catch (int s) // 故意写错类型
	{
		cout << s << endl;
	}

	return 0;
}

现在的编译器都很智能,如果你在代码编写阶段一个 catch 块都没写,会直接报语法错误,所以一定要确保抛出的异常,能被正确捕获


catch 块至少得存在一个,也可以存在多个,当同时存在多个 catch 块时,抛出的异常会根据栈帧顺序,被最近的 catch 块捕获

catch 块只能进入一次,异常被捕获后,无法再进入其他 catch

注意: 如果出现多个类型不匹配的 catch 块时,异常会被类型匹配,且最近的 catch 块捕获

void divisor(int x, int y)
{
	if (y == 0)
	{
		// 除 0 错误,抛出异常
		throw("除数(分母)不能为0");
	}

	cout << "结果:" << x / y << endl;
}

void calc()
{
	int x = 10;
	int y = 0;

	try
	{
		divisor(x, y);
	}
	catch (const char* s)
	{
		cout << "void calc()" << endl;
		cout << s << endl;
	}
}

int main()
{
	try
	{
		calc();
	}
	catch (const char* s)
	{
		cout << "int main()" << endl;
		cout << s << endl;
	}

	return 0;
}

divisor 函数捕获异常后,main 函数中不再捕获异常,代码正常运行结束;一般异常捕获这个工作会交给最外层统一处理,比如这里的 main 函数,此时如果出现了异常,代码会直接跳转至 main 函数中,至于中间的栈帧会被 throw 自动清理

void divisor(int x, int y)
{
	if (y == 0)
	{
		// 除 0 错误,抛出异常
		throw("除数(分母)不能为0");
	}

	cout << "结果:" << x / y << endl;
}

void calc()
{
	int x = 10;
	int y = 0;

	divisor(x, y);
}

int main()
{
	try
	{
		calc();
	}
	catch (const char* s)
	{
		cout << "int main()" << endl;
		cout << s << endl;
	}

	return 0;
}


在实际使用中,并不会这样直接抛出一个字符串,而是构建一个 异常信息类,抛出一个 异常对象,类中包罗万象,需要包含最基本的两个信息:错误码、错误信息

// 异常信息类
class Exception
{
public:
	Exception(int errcode, const string& content)
		:_errno(errcode), _content(content)
	{}
	
	void what() const
	{
		// 打印异常信息
		cout << "发生了异常" << endl;
		cout << "\t错误码为: " << _errno << endl;
		cout << "\t错误信息为: " << _content << endl;
	}
private:
	int _errno = 0;
	string _content;
};

这样一来,在出现异常时,可以构建一个异常对象并抛出

为什么要设计错误码?
因为在某些场景中,不方便直接暴露错误,比如消息发送过程中,如果遇到网络问题,检测到错误码为 x,会不断重试,直到发送成功或超时,这样能使用户体验更好

throw(Exception(3, "除数(分母)不能为0"));

// 现在引用的是临时对象
catch(const Exception& e);

注意: catch 块捕捉时,不可以直接使用左值引用,因为抛出的是一个局部对象


当出现未知异常时,如何解决?

通过 catch(...) 捕获,支持捕获任意类型的异常

void calc()
{
	// 故意抛出一个未知异常
	throw(10);
}

int main()
{
	try
	{
		calc();
	}
	catch (const Exception& e)
	{
		cout << "int main()" << endl;
		e.what();
	}
	catch (...)
	{
		cout << "int main()" << endl;
		cout << "未知异常" << endl;
	}

	return 0;
}

catch(...) 就相当于异常捕获的底线,如果前面的 catch 块都无法捕获异常,此时就轮到 catch(...) 登场,避免程序因异常无法捕获而终止


异常支持使用父类指针/引用捕获子类对象,假设当前项目中存在:网络异常、数据异常、SQL异常 等多种异常信息类,如果想让最外层的 catch 块捕获所有异常对象,可以让这些异常信息类都继承自同一个父类,同时重写父类中的虚函数,再通过父类指针/引用捕获

#include <iostream>
#include <string>
#include <windows.h>

using namespace std;

// 父类
class Exception
{
public:
	Exception(int errcode, const string& content)
		:_errno(errcode), _content(content)
	{}

	virtual string what() const
	{
		return to_string(_errno) + " : " + _content;
	}

protected:
	int _errno = 0;
	string _content;
};

// 网络子类
class HttpException : public Exception
{
public:
	HttpException(int errcode, const string& content, const string& url, const string& type)
		:Exception(errcode, content)
		, _url(url)
		, _type(type)
	{}

	// 重写
	virtual string what() const
	{
		return to_string(_errno) + " : " + _content + " ---> " + _type + " " + _url;
	}

private:
	string _url; // 资源路径
	string _type; // 请求类型
};

// 内存子类
class CacheException : public Exception
{
public:
	CacheException(int errcode, const string& content)
		:Exception(errcode, content)
	{}

	// 重写
	virtual string what() const
	{
		return to_string(_errno) + " : " + _content;
	}
};

// SQL子类
class SqlException : public Exception
{
public:
	SqlException(int errcode, const string& content, const string& sql)
		:Exception(errcode, content)
		,_sql(sql)
	{}

	// 重写
	virtual string what() const
	{
		return to_string(_errno) + " : " + _content + " ---> " + _sql;
	}

private:
	string _sql; // SQL语句
};

void SqlServe()
{
	int n = rand();
	if (n % 5 == 3)
		throw(SqlException(3, "数据表不存在", "select * from t2"));
	else if (n % 5 == 4)
		throw(SqlException(4, "权限不足", "drop table t1"));

	// 操作完成
	cout << "请求已完成" << endl << "-------------------" << endl;
}

void CacheServe()
{
	int n = rand();
	if (n % 5 == 2)
		throw(CacheException(2, "数据不存在"));

	// 进入下一层
	SqlServe();
}

void HttpServe()
{
	int n = rand();
	if (n % 5 == 0)
		throw(HttpException(0, "请求的资源不存在", "/index.html HTTP/1.1", "GET"));
	else if (n % 5 == 1)
		throw(HttpException(1, "没有访问权限", "/image.html HTTP/1.1", "POST"));

	// 进入下一层
	CacheServe();
}

int main()
{
	srand((size_t)time(nullptr));

	while (true)
	{
		Sleep(1000);
		try
		{
			cout << "开始请求" << endl;
			HttpServe();
		}
		catch (const Exception& e)
		{
			// 异常处理
			cout << e.what() << endl << "===================" << endl;
		}
		catch (...)
		{
			cout << "未知异常" << endl;
		}
	}

	return 0;
}

这里用到了 继承 + 多态 相关知识,当子类对象赋值给父类指针/引用时,会触发 切片 机制,这个过程是天然发生的,所以但凡是从该父类派生出的子类对象,都可以被正常接收

这种玩法在实际开发中非常实用,项目组可以根据自己的需求,设计继承体系,以及异常体系

注意: 如果同时存在类型为父类及子类的 catch 块,异常会被较近的 catch 块捕捉

2.2.异常的重新抛出

异常抛出后,可能会导致某些栈帧中的代码没有被执行,从而引发内存泄漏等问题,比如下面场景中就出现了内存泄露问题

// 异常信息类
class Exception
{
public:
	Exception(int errcode, const string& content)
		:_errno(errcode), _content(content)
	{}

	string what() const
	{
		return to_string(_errno) + " : " + _content;
	}
public:
	int _errno = 0;
	string _content;
};

void divisor(int x, int y)
{
	if (y == 0)
	{
		// 除 0 错误,抛出异常
		Exception e(3, "除数(分母)不能为0");
		throw(e);
	}

	cout << "结果:" << x / y << endl;
}

void calc()
{
	int x = 10, y = 0;

	int* arr = new int[10];

	divisor(x, y);

	delete[] arr;
	cout << "delete[] arr: " << arr << endl;
}

int main()
{
	try
	{
		calc();
	}
	catch (const Exception& e)
	{
		cout << "int main()" << endl;
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "int main()" << endl;
		cout << "未知异常" << endl;
	}

	return 0;
}


可以看到,动态开辟的空间并没有被正确释放,这是因为异常抛出后,throw 会清理 calc 的栈帧,导致其中的代码没有被执行,要想正确的释放内存,需要在 calc 函数中主动捕获异常,将空间释放后,重新抛出异常

注:throw 表示捕获到什么异常,就抛出什么异常

void calc()
{
	int x = 10, y = 0;

	int* arr = new int[10];

	try
	{
		divisor(x, y);
	}
	catch (...)
	{
		delete[] arr;
		cout << "delete[] arr: " << arr << endl;

		throw;
	}

	delete[] arr;
	cout << "delete[] arr: " << arr << endl;
}

现在空间被释放了,同时异常正常交给了最外层处理,不过这种写法的代码不容易维护,好在 C++ 中诞生了 智能指针,能自动释放空间,这也是下一篇博客的内容

为什么异常要在统一的地方进行处理?

  • 统一记录日志
  • 针对某些错误进行额外处理

2.3.异常安全

异常在使用时需要注意以下几点

1.最好不要在构造函数中抛出异常,因为对象的构造和初始化是需要时间的,如果在构造途中抛出了异常,会导致对象构造不完整

2.最好不要在析构函数中抛出异常,析构函数清理资源的过程同样需要时间,析构途中抛出异常可能会引发内存泄漏

3.在使用诸如 new/deletemalloc/freefopen/fcloselock/unlock 等资源管理配套函数时,需要特别注意资源泄漏或者死锁问题,在发生捕获到异常后,需要先把资源释放了,再考虑异常处理

2.4.异常规范

异常就像一只薛定谔的猫,你永远不知道别人是否抛出、何时抛出,为了让异常的使用更加规范,C++98 标准规定:

  1. 可以在函数的后面接 throw(type1, type2, type3 ...),列出这个函数可能抛掷的所有异常类型
  2. 函数的后面接 throw( ),表示该函数不会抛出异常
  3. 若无异常接口声明,则此函数可以抛掷任何类型的异常

比如这样编写函数:

void func1() throw(int, char, string); // 可能抛出这三种类型的异常

void func2() throw(); // 该函数不会抛出异常

void func3(); // 该函数可以抛出任何类型的异常

在标准库函数中,就采用了这种规范写法

C++98 中的异常规范过于繁琐,由于异常规范并非强制性语法,实际使用过程中有很多人都不会遵守,于是在 C++11 中对异常规范进行了相关更新,化繁为简,只需使用一个 noexcept 关键字表明该函数不会抛出异常

void func2() noexcept; // 该函数不会抛出异常

推荐使用 C++11 中新方案,对于不会抛出异常的函数,使用 noexcept 关键字修饰

  • noexcept 可以检测当前函数中是否发生了 throw 抛出异常的行为

如果加了 noexcept 关键字后,函数仍然抛出异常,是否会报错?
答案是会的,会直接被 abort 函数终止进程,所以可以放心使用 noexcept 关键字;即便是在异常抛出与异常捕获的中间函数中使用 noexcept 修饰,在异常抛出后,进程也会被终止;总之就是加了 noexcept 修饰后,所有该函数涉及的操作,都不会出现异常

注:如果使用 throw() 修饰,仍然抛出异常后,进程不会终止,所以还是推荐使用 noexcept


3.异常体系

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

C++ 标准库中提供了一套 异常体系,其中包含了各种常见异常,我们也可以继承 std::exception 父类,重写其中的虚函数,实现其他方面的异常

异常描述
std::exception该异常是所有标准C++异常的父类
std::bad_alloc该异常可以通过new抛出
std::bad_cast该异常可以通过dynamic_cast抛出
std::bad_typeid该异常可以通过typeid抛出
std::bad_exception这在处理C++程序中无法预期的异常时非常有用
std::logic_error理论上可以通过读取代码来检测到的异常
std::runtime_error理论上不可以通过读取代码来检测到的异常
std::domain_error当使用了一个无效的数学域时,会抛出该异常
std::invalid_argument当使用了无效的参数时,会抛出该异常
std::length_error当创建了太长的std::string时,会抛出该异常
std::out_of_range该异常可以通过方法抛出,例如std::vector和std::bitset<>::operator
std::overflow_error当发生数学上溢时,会抛出该异常
std::range_error当尝试存储超出范围的值时,会抛出该异常
std::underflow_error当发生数学下溢时,会抛出该异常

3.2.自定义异常体系

虽然 C++ 标准库中提供了标准异常体系,但实际上大多数公司会根据实际项目定义自己的异常体系,比如之前的 SqlException 等异常信息类,就属于自定义异常体系

为什么要自定义异常体系?
因为公司中的项目一般都会进行模块划分,不同的模块用于实现不同的功能,如果不通过自定义异常体系来规范异常行为,会导致整个项目的异常处理及其麻烦,有了自定义异常体系后,只需要通过一个父类指针/引用,即可捕获不同子类对象异常,统一进行处理


4.异常的优缺点

异常的优点

  • 可以展示更丰富的错误信息,更好的定位程序 Bug
  • 错误码是层层返回的,不方便定位问题,而异常是则直接被捕获的
  • 很多的第三方库都包含了异常,需要与其进行兼容,比如 boostgtestgmock
  • 部分函数使用异常更好表示错误,比如 T& operator[](size_t pos) 如果越界了就抛异常,而不是返回 T() 或 断言

异常的缺点

  • 执行流跨度过大,并且非常混乱,导致跟踪调试程序时比较困难
  • 异常有一些性能上的开销(当代硬件速度很快,可以忽略不计)
  • C++ 没有垃圾回收机制,资源需要自己管理,可以使用 RAII 来处理资源管理问题
  • C++ 标准库的异常体系定义不够好,导致出现了各种异常体系,比较混乱
  • 异常尽量规范使用,否则后果不堪设想
    1. 抛出异常类型都继承自一个基类
    2. 函数是否抛异常可以使用 noexcept 注明

总体而言,异常 利大于弊,在工程项目中鼓励使用异常,OO 语言基本都是使用异常处理错误,这是大势所趋


🌆总结

以上就是本次关于C++『异常』的全部内容了,异常处理是软件开发中重要的错误管理工具,本文深入探讨了异常的基本概念、C++中的处理方式、使用技巧和异常体系设计。尽管异常提供了丰富的错误信息,但其使用需要谨慎考虑执行流、性能开销等因素。在面对项目需求时,程序员应权衡利弊,以确保异常处理在提高代码可维护性和可靠性方面发挥最佳效果


星辰大海

相关文章推荐

C++ 进阶知识

C++11『lambda表达式 ‖ 线程库 ‖ 包装器』

C++11『右值引用 ‖ 完美转发 ‖ 新增类功能 ‖ 可变参数模板』

C++11『基础新特性』

C++11『右值引用与移动语义』

C++11『基础新特性』

C++ 哈希的应用【布隆过滤器】

C++ 哈希的应用【位图】

C++【哈希表的完善及封装】

C++【哈希表的模拟实现】

C++【初识哈希】

C++【一棵红黑树封装 set 和 map】

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

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

相关文章

2023济南大学acm新生赛题解

通过答题情况的难度系数&#xff1a; 签到&#xff1a;ACI 铜牌题&#xff1a;BG 银牌题&#xff1a;EF 金牌题&#xff1a;DHJKO 赛中暂未有人通过&#xff1a;LMNP A - AB Problem 直接根据公式计算就行。 #include<stdio.h> int main(){int a,b;scanf("%…

综合练习(OSPF+BGP+ISIS+单臂路由)

一、知识补充 1、链路聚合 为了满足不同服务器之间的数据交互&#xff0c;交换机之间必须具有更高的带宽&#xff0c;同时需要具备链路的冗余备份&#xff1b; 通常可以增加核心交换机之间的物理链路数量&#xff0c;以提高核心交换机之间的链路带宽&#xff1b;在启用STP的…

Flutter视频播放器在iOS端和Android端都能实现全屏播放

Flutter开发过程中&#xff0c;对于视频播放的三方组件有很多&#xff0c;在Android端适配都挺好&#xff0c;但是在适配iPhone手机的时候&#xff0c;如果设置了UIInterfaceOrientationLandscapeLeft和UIInterfaceOrientationLandscapeRight都为false的情况下&#xff0c;无法…

怎么用vue创建一个项目,并使用Element组件

要使用element组件的话要先安装一个node&#xff0c;然后再输入下面一系列指令安装vue脚手架&#xff0c;这样比较方便 然后输入vue iu指令进入可视化界面创建项目&#xff0c;创建项目的操作流程我已经做好放在下面了&#xff0c;此处省略..............N个字...............&…

【USB、串口、COM口、TTL、RS-232、RS-485区别详解】

USB&#xff0c;串口&#xff0c;COM口&#xff0c;TTL&#xff0c;RS-232&#xff0c;RS-485区别详解 1. USB&#xff0c;串口&#xff0c;COM口&#xff0c;TTL&#xff0c;RS-232&#xff0c;RS-485区别详解2 USB转TTL2 RS-232转TTL3 USB4 UART5 STM32串口异步通讯需要定义的…

Stable Diffusion 系列教程 - 2 WebUI 参数详解

Stable Diffusion 的整个算法组合为&#xff1a; UNet VAE 文本编码器 UNet&#xff1a;就是我们大模型里的核心。 文本编码器&#xff1a;将我们的prompt进行encoder为算法能理解的内容&#xff08;可以理解为SD外包出去的项目CLIP&#xff09;。 VAE&#xff1a;对UNet生…

Linux权限理解

文章目录 前言概述Linux下的权限Linux权限管理文件访问者的分类&#xff1a;属性&#xff1a;文件权限值表示方法&#xff1a; 文件类型&#xff1a; 权限的修改chmod对 text.txt 文件的权限进行修改法1&#xff1a;法2&#xff1a; chownchgrpumaskfile指令目录权限粘滞位 前言…

socket 套接字

1、套接字介绍 socket起源于Unix&#xff0c;遵循“一切皆文件”出发点&#xff0c;都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。 在设计模式中&#xff0c;Socket把复杂的TCP/IP协议族隐藏在Socket接口后面&#xff0c;Socket去组织数据&#xf…

端口复用和重映射

一、端口复用 &#xff08;1&#xff09;端口复用概念 端口复用是将一个I/O赋予多个功能&#xff0c;通过设置I/O的工作模式来切换不同的功能。 STM32有很多的内置外设&#xff0c;这些外设的外部引脚都是与GPIO复用的。也就是说&#xff0c;一个GPIO如果可以复用为内置外设的…

【rabbitMQ】rabbitMQ用户,虚拟机地址(添加,修改,删除操作)

rabbitMQ的下载&#xff0c;安装和配置 https://blog.csdn.net/m0_67930426/article/details/134892759?spm1001.2014.3001.5502 rabbitMQ控制台模拟收发消息 https://blog.csdn.net/m0_67930426/article/details/134904365?spm1001.2014.3001.5502 目录 用户 添加用户…

simulink MATLABFunction模块中实时函数调用函数的使用

样例 function Predyy matlabceshi(input, Time_s) input1 input; Time_s1 Time_s; Predyy ee(input1) mm(Time_s1); end 上面是主要部分&#xff0c;下面是被调用部分 function A ee(input1) A input1 * 100; end function B mm(Time_s1) B Time_s1 * 100; end 模型…

jvm内存分配机制

内存分配机制 1.一般机制 ​ 如果对象在Eden出生并且经过第一次Minor后仍然存活,并且能被survivor容纳的话,将被移动到survivor空间中,并将对象年龄设为1 对象每熬过一次MinnorGC,年龄就增加一岁,当他的年龄增加到一定程度(默认15岁)就会被晋升到老年代 直接进入老年代的情…

sap增强

四代增强 2种显示增强1种隐式增强 隐式增强 光标放在增强点或其中的代码点击修改即可修改代码 显示增强 1.ENHANCEMENT-POINT 在代码修改界面选择空行 光标所在位置 可以创建多个增强实施且激活后都会执行. 2.ENHANCEMENT-SECTION 1,选中程序中空行 2.编辑->创建选项 …

「JavaScript每日一练」系列——提高你的JS技能(第三天)

有什么不懂可以去看我前两天的笔记 https://blog.csdn.net/weixin_70007095/article/details/134905674 目录 有什么不懂可以去看我前两天的笔记 JavaScript 进阶 - 第3天笔记 编程思想 面向过程 面向对象 构造函数 原型对象 constructor 属性 对象原型 原型继承 原型链 JavaSc…

肾精不足,湿浊难去!老中医教你简单1招,藏养肾精、祛寒祛湿!

前言 冬季祛湿养肾精&#xff0c;做好2件事能事半功倍 湿毒是慢病的温床 有「七十二变」的法力 如果我们善于观察大自然&#xff0c;那么就会发现&#xff0c;在阴雨天&#xff0c;尤其在南方的回南天之时&#xff0c;那些隐蔽的草丛或者腐朽的木桩之上&#xff0c;会长出很…

排序算法-选择/堆排序(C语言)

1基本思想&#xff1a; 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的 数据元素排完 。 2 直接选择排序: 在元素集合 array[i]--array[n-1] 中选择关键码最大 ( 小 ) 的数据元素…

AI:99-基于深度学习的飞机故障检测与维修

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的核心代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新…

HarmonyOS应用开发者基础认证考试(稳过)

判断题 ​​​​​​​ 1. Web组件对于所有的网页都可以使用zoom(factor: number)方法进行缩放。错误(False) 2. 每一个自定义组件都有自己的生命周期正确(True) 3. 每调用一次router.pushUrl()方法&#xff0c;默认情况下&#xff0c;页面栈数量会加1&#xff0c;页面栈支持的…

【开源】基于Vue+SpringBoot的固始鹅块销售系统

项目编号&#xff1a; S 060 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S060&#xff0c;文末获取源码。} 项目编号&#xff1a;S060&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 鹅块类型模块2.3 固…

关于对向量检索研究的一些学习资料整理

官方学习资料 主要是的学习资料是&#xff0c; 官方文档 和官方博客。相关文章还是挺多 挺不错的 他们更新也比较及时。有最新的东西 都会更新出来。es scdn官方博客 这里简单列一些&#xff0c;还有一些其他的&#xff0c;大家自己感兴趣去看。 什么是向量数据库 Elasticse…