C++异常详解

文章目录

  • 前言
  • 一、回顾C语言
  • 二、异常的概念
  • 三、异常的使用
    • 1.异常的抛出和捕获
    • 2.异常的重新捕获
  • 三.异常安全与异常规范
    • 1.异常安全
    • 2.异常规范
  • 四.自定义异常体系
  • 五.C++标准库的异常体系
  • 六.异常优缺点
  • 练习题
  • 总结


前言

在本篇文章中,我们将会详细介绍一下有关C++异常的讲解,主要涉及异常的使用,应用场景,异常的优缺点等方面,同时有了异常才会引出我们之后学习的一个非常重要的知识点————智能指针。

一、回顾C语言

c语言解决错误时一般是通过返回错误码的方式,如果遇到非常严重的错误,就会终止程序。

🌟 终止程序:比如我们遇到除零错误,内存错误,空指针的解引用等操作,我们的程序就会终止,程序终止也意味着进程的终止,这有可能会导致大量重要数据的丢失,这是非常严重的。
🌟 返回错误码:也可以通过返回错误码的方式告诉我们相应的错误,但这只是错误码,我们还需要根据错误码查找相关的错误信息,再进一步的分析程序,才能得出具体的错误信息,这时很不便利的。很多库函数都是把错误信息放到error中,表示错误。

二、异常的概念

我们需要程序告诉是是什么错误。

我们C++在解决错误时采用的方法时异常

当函数发现了自己无法处理的错误时抛异常,让函数直接或者间接的调用者处理这个异常。

我们将会引入三个关键字

🌟 throw:出错时,用于抛异常(可在任意位置抛出)
🌟 try:里面放可能抛异常的代码,其中这里的代码称为保护代码,后面跟着catch
🌟 catch:在想要处理的地方,通过异常处理程序捕获异常,可以有多个catch进行捕获

throw…………

try
{
}
catch(………)
{
}
catch(……)
{
}

其实异常就存在在我们的日常生活中,就比方说微信在网络不好的时候,会出现一个感叹号告诉你消息发不出去,此时的程序就是在抛异常,告诉你当前网络状态不佳。

如果这个异常按照C语言对错误的处理方式进行操作,整个微信进程就会直接崩溃,强行退出。而采用C++的抛异常机制,将抛出的异常捕获,然后处理,比如当出现网络不好抛异常时,微信就会采取尝试多次发送这样的操作,整个微信程序也不会退出。

正式由于在实际中,很多情况下我们是不希望只要产生异常就直接终止的整个进程的,通过抛异常和捕获处理异常的手段便可让程序保持运行。

三、异常的使用

1.异常的抛出和捕获

异常的抛出和匹配原则

🌟异常通过抛出对象引发,这个对象与catch中()中类型进行匹配,这个对象可以是内置类型,也可以是自定义类型。


#include <iostream>
using namespace std;
double  Division(int x, int y)
{
	//除零错误,抛异常
	if (y == 0)
	{
		throw "Division by zero condition!";
	}
	else
	{
		return ((double)x / (double)y);
	}
}

void fun()
{
	int a;
	int b;
	cin >> a >> b;
	cout << Division(a, b) << endl;
}
int main()
{
	try
	{
		fun();
	}
	catch (const char*errmsy)
	{
		cout << errmsy << endl;
	}
	return 0;
}

我们运行运行抛异常看一下
在这里插入图片描述
我们确实捕捉到了,打印的就是throw抛的内容。

🌟 我们再匹配相应的catch代码时,如果有多个都满足,选取与throw类型匹配且距离较近的catch.

调用链是指函数栈帧建立的先后顺序,就比如下面代码中main函数优先建立栈帧,然后func函数建立栈帧,最后division函数建立栈帧,这样的顺序就叫调用链。

举个例子说明一下

#include <iostream>
using namespace std;
double  Division()
{
	int a;
	int b;
	cin >> a >> b;
	if (b == 0)
	{
			throw "Division by zero condition!";
	}
	else
	{
			return ((double)a / (double)b);
	}
}


void fun()
{
	try
	{
		Division();
	}
	catch (const char* errmsy)
	{
		cout <<"void fun()" << errmsy << endl;
	}
}
int main()
{
	try
	{
		fun();
	}
	catch (const char* errmsy)
	{
		cout <<"int main()" << errmsy << endl;
	}
	return 0;
}

main函数调用了fun函数,fun函数调用了Division函数。
main函数中catch与fun函数中的catch都与Division中的throw类型匹配,那么他会调用哪个呢??

我们抛异常来看一下
在这里插入图片描述
我们发现调用的是fun中的catch,因为两个都满足。所以找最近的那一个,就是fun函数了。
🌟抛出异常对象后,会生成一个异常对象的拷贝,并不是直接传递给catch()里面的对象,而是将throw对象的拷贝传给catch()中的对象,这个拷贝的临时对象会被catch后销毁。


#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{
	//除零错误,抛异常
	if (y == 0)
	{
		string s("Division by zero condition!");
		throw s;
	}
	else
	{
		return ((double)x / (double)y);
	}
}

void fun()
{
	int a;
	int b;
	cin >> a >> b;

	cout << Division(a, b) << endl;
}
int main()
{
	try
	{
		fun();
	}
	catch (const string ret)
	{
		cout << ret << endl;
	}
	return 0;
}

我们抛出的是一个string的临时对象s,出了作用域销毁了,如果是直接传给外边,传递不过去。
如果我们抛异常了,打印的还是Division by zero condition!,就能够说明生成了拷贝。

我们测试一次看看
在这里插入图片描述
我们发现确实存在拷贝,这和函数参数的传递有异曲同工之妙。

由于临时对象具有常性,所以当抛出的对象是指针时一定注意在形参上加上const才能被接收。这也解释了上面的代码中为什么error_message的类型为什么是const char而不是char
🌟我们一般在catch最后边加上一个catch(…),表示这个catch可以匹配任意类型,但是不知道异常错误是什么。
我们这个东西可以解决很大的问题,如果我们的异常没有对应的catch,就会报错,如果放到程序中,就会崩溃。

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{
	//除零错误,抛异常
	if (y == 0)
	{
		throw "Division by zero condition!";
	}
	else if(y==1)
	{
		return ((double)x / (double)y);
	}
	else
	{
		throw 1;
	}
}

void fun()
{
	int a;
	int b;
	cin >> a >> b;

	cout << Division(a, b) << endl;
}
int main()
{
	try
	{
		fun();
	}
	catch (const string ret)
	{
		cout << ret << endl;
	}
	catch (...)
	{
		cout << "Unkown error" << endl;
	}
	return 0;
}

我们如果输入的第二个数为10,就会抛异常,类型为整形,但是外边没有对应的类型匹配,就会匹配到(…)中。
在这里插入图片描述
🌟有一种特殊情况,不用类型匹配:可以抛出子类对象,使用父类对象再catch中进行捕获,这个在实际中是非常有用的。

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

🌟1.查看throw是否在try中,如果在,就进行异常捕获。
🌟2.如果存在匹配的catch,就调到对应的catch进行处理。
🌟3.如果这一层没有匹配的catch,退出当前栈,就到上一层中进行寻找。
🌟4.如果再main函数中,也不存在匹配的catch就报错,
沿着调用链查找匹配catch子句的过程称为栈展开。
🌟5.异常捕获与exit不同。exit直接退出程序,catch处理完异常之后,catch后面的代码会正常执行。

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{
	//除零错误,抛异常
	if (y == 0)
	{
		throw "Division by zero condition!";
	}
	else if (y == 1)
	{
		return ((double)x / (double)y);
	}
}

void fun()
{
	int a;
	int b;
	cin >> a >> b;
	cout << Division(a, b) << endl;
}
int main()
{
	try
	{
		fun();
	}
	catch (const string ret)
	{
		cout << ret << endl;
	}
	catch (...)
	{
		cout << "Unkown error" << endl;
	}

	cout << "异常处理后继续执行相关代码" << endl;
	return 0;
}

在这里插入图片描述

2.异常的重新捕获

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

我们捕获这个异常并不是为了处理这个异常,而是为了干一些其他的事情之后,再把这个异常抛给上层,继续处理。

我们举一个例子理解一下


#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{
	//除零错误,抛异常
	if (y == 0)
	{
		throw "Division by zero condition!";
	}
	else
	{
		return ((double)x / (double)y);
	}
}

void fun()
{
	int*p = new int[10];
	int a;
	int b;
	cin >> a >> b;
	cout << Division(a, b) << endl;
	cout << "delete[] p" << endl;
	delete[] p;
}
int main()
{
	try
	{
		fun();
	}
	catch (const string ret)
	{
		cout << ret << endl;
	}
	catch (...)
	{
		cout << "Unkown error" << endl;
	}
	return 0;
}

在fun函数中new了一块空间,现在抛异常看一下


我们new的空间并没有被释放,发生了内存泄漏

这时就需要用到异常的重新捕获了,我们需要在fun函数中对这个异常进行一次捕获,释放new的空间,再抛给main函数。
为我们也可以直接在fun中捕获异常,不在main中处理呀??
而在我们对异常进行一部分操作时,我们更愿意让所有异常在main函数中进行统一处理,比如对异常进行记录日志这样的操作,此时需要重新使用throw抛出异常。

我们看一下解决代码

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{
	//除零错误,抛异常
	if (y == 0)
	{
		throw "Division by zero condition!";
	}
	else
	{
		return ((double)x / (double)y);
	}
}

void fun()
{
	int*p = new int[10];
	int a;
	int b;
	cin >> a >> b;
	try
	{
		cout << Division(a, b) << endl;
	}
	catch (...)
	{
		cout << "delete[] p" << endl;
		delete[] p;
		throw;//捕到什么抛什么
	}
	cout << "delete[] p" << endl;
	delete[] p;
}
int main()
{
	try
	{
		fun();
	}
	catch (const string ret)
	{
		cout << ret << endl;
	}
	catch (...)
	{
		cout << "Unkown error" << endl;
	}
	return 0;
}

运行看一下
在这里插入图片描述

一旦catch捕获异常,不能将异常用throw语句再次抛出,这句话是不对的。

三.异常安全与异常规范

1.异常安全

🌟构造函数时用来构造对象和初始化的,最好不要在构造函数抛异常,可能会导致对象不完整或者没有完全初始化。

🌟析构函数是用来释放空间的,对资源进行清理。最好不要在析构函数抛异常,可能会导致资源泄露(内存泄漏,句柄未关闭)。
🌟C++中异常经常会导致资源泄露问题,比如在new和delete中抛异常,导致内存泄漏,在lock和unlock之间抛出异常1导致死锁等问题,我们将会用智能指针来解决这个问题。

2.异常规范

我们在书写异常时,可以抛出任意类型的对象, 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。

C++98,建议,并不会强制报错,如果我们不这样做了只会有警告,这个是为了兼容c语言做出的让步。

🌟我们可以在函数头后买你加上throw( ),括号里存放可能抛出的异常类型。
throw(char,int char*)就表明了这个函数可能抛出char,int,char这三种类型的异常。
🌟这里表示这个函数只会抛出bad_alloc的异常
void
operator new (std::size_t size) throw (std::bad_alloc);
🌟throw(),表明这个函数只会不会抛出异常
🌟如果无异常接口声明,此函数可以抛任意类型的异常

但是有些异常类型是非常复杂的,为了写出可能发生的异常类型,代价会很大,而且写时太繁琐写出来也不美观。因此,这个建议性的规范很少有人用,也正因为它只是一个建议,所以不使用或者不按要求使用也不会报错。

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y) throw()//表明不会抛异常
{
	//除零错误,抛异常
	if (y == 0)
	{
		throw "Division by zero condition!";
	}
	else
	{
		return ((double)x / (double)y);
	}
}

void fun()
{
	int a;
	int b;
	cin >> a >> b;
	 cout << Division(a,b) << endl; 
}
int main()
{
	try
	{
		fun();
	}
	catch (const string ret)
	{
		cout << ret << endl;
	}
	catch (...)
	{
		cout << "Unkown error" << endl;
	}
	return 0;
}

但是如果我们硬要抛异常呢??
在这里插入图片描述
只会存在警告,不会强制报错。

C++11

🌟noexcept,表明函数不会抛异常
但如果我们还是硬抛异常会怎样呢???

在这里插入图片描述
代码就给我们直接挂掉了。

四.自定义异常体系

在以后写代码的时候会遇到小组合作的形式,每个小组负责不同的模块,每个小组都会抛出异常,但是每个小组抛出的异常类型不同,放在一起在main函数中进行捕捉就会很复杂。
实际中抛出和捕获的匹配原则有一个例外,类型可以不完全匹配,抛出子类对象用父类进行捕捉。每个小组都可以抛出派生类的异常,在mian函数中使用基类统一捕捉。

实际项目中,可以创建一个父类,父类中有一个错误码(int)和错误描述信息(string),在这个类里面创建一个虚函数用于返回内部的string对象方便使用人员打印。

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()
{
	// ...
	srand(time(0));
	if (rand() % 3 == 0)
	{
		throw HttpServerException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpServerException("权限不足", 101, "post");
	}
	CacheMgr();
}
int main()
{
	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));
		try {
			HttpServer();
		}

		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}
	return 0;
}

main函数中首先调用httpserver函数,生成一个随机数,我们把这个数字看作一种情况,如果这个数字能被3 和4整除那么这种情况就出错了,直接使用throw来抛出异常,如果没有出错的花就调用缓冲区的函数,这个函数里面也是相同道理,在这个函数之后就调用数据库的相关函数遇到一些情况就抛出异常,那么在main函数里面我们就可以统一使用父类类型的catch来统一捕获异常,并使用里面的what函数来打印出从的内容,那么上面的代码运行的结果如下:

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

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

在这里插入图片描述

在这里插入图片描述
由于C++提供的异常体系对项目开发中的异常帮助十分有限,所以这个标准几乎没人用。’

六.异常优缺点

优点

🌟1.相比于错误码,异常可以清晰准确的展现出错误信息,甚至包含堆栈调用的信息,帮助我们更好的定位bug
🌟2.异常会进行多层跳转,不用层层返回进行判断,直到最外层拿到错误。


int ConnnectSql()
{
	// 用户名密码错误
	if (...)
		return 1;

	// 权限不足
	if (...)
		return 2;
}

int ServerStart() {
	if (int ret = ConnnectSql() < 0)
		return ret;
	int fd = socket()
		if(fd < 0return errno;
}

int main()
{
	if (ServerStart() < 0)
		...

		return 0;
}

下面这段伪代码我们可以看到ConnnectSql中出错了,先返回给ServerStart,ServerStart再返回给main函数,main函数再针对问题处理具体的错误。

如果是异常体系,不管是ConnnectSql还是ServerStart及调用函数出错,都不用检查,因为抛出的异常异常会直接跳到main函数中catch捕获的地方,main函数直接处理错误。
🌟2.很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常
🌟4.部分函数使用异常更好处理。
T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误

缺点:

🌟1.导致执行流乱跳,导致我们追踪调式以及分析程序时,比较困难。
🌟2.异常会有一些性能的开销,。当然在现代硬件速度很快的情况下,这个影响基本忽略不计。
🌟3.C++没有垃圾回收机制,资源需要自己管理,有可能发生内存泄漏,死锁等安全问题。
🌟4.C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
🌟异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。

练习题

如何捕获异常可以使得代码通过编译? ()

class A

{
public:
  A(){}
};

void foo()
{  throw new A; }

A.catch (A x)
B.catch (A * x)
C.catch (A & x)
D.以上都不是

正确答案是B, 异常是按照类型来捕获的,throw后抛出的是A*类型的异常,因此要按照指针方式进行捕获

总结

以上就是今天要讲的内容,本文仅仅详细介绍了C++异常的内容。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘

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

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

相关文章

Redis数据结构扩容源码分析

1 Redis数据结构 redis的数据存储在dict.中&#xff0c;其数据结构为(c源码) ypedef struct dict { dictType *type; //理解为面向对象思想&#xff0c;为支持不同的数据类型对应dictType抽象方法&#xff0c;不同的数据类型可以不同实现 void *privdata; //也可不同的数据类…

C++中vector的简单实现

文章目录 一、主要任务1. 查看文档的网站的链接2.内部模拟的函数 二、本人的模拟实现过程1. 所需模拟实现的函数a.构造、拷贝构造b. reverse()扩容c.insert()、push_back()插入数据d. erase()、pop_back()删除数据e. swap()交换f. begin()、end()非const与const迭代器g. 完善构…

【ARM 嵌入式 C 入门及渐进 16.1 -- C 代码实现CRC32校验函数】

请阅读【嵌入式开发学习必备专栏】 文章目录 CRC32校验函数CRC32 表与函数CRC32 测试函数测试结果 对比测试结果 CRC32校验函数 在C语言中&#xff0c;实现CRC32计算的函数需要一个CRC算法的实现。以下是一个使用查表法实现CRC32的简单例子。这种方法通过预先计算好的CRC表来快…

六级翻译笔记

理解加表达 除了专有名词不能自己理解翻译&#xff0c;其它都可以 时态一般唯一 题目里出现有翻译为 客观存在&#xff1a; there be 单词结尾加er和ee的区别&#xff1a;er是主动&#xff0c;ee是被动 中文句子没有被动&#xff0c;也可以英文翻译为被动 中文的状语可以不是…

python代码实现TF-IDF

1、TF-IDF解释 TF-IDF&#xff08;Term frequency–inverse document frequency&#xff09;&#xff0c;中文翻译就是词频 - 逆文档频率&#xff0c;是一种用来计算关键词的传统方法。 TF&#xff08;Term Frequency&#xff09;&#xff1a;TF 的意思就是词频&#xff0c;是…

图片批量管理迈入智能新时代:一键输入关键词,自动生成并保存惊艳图片,轻松开启创意之旅!

在数字化时代&#xff0c;图片已成为我们表达创意、记录生活、传递信息的重要工具。然而&#xff0c;随着图片数量的不断增加&#xff0c;如何高效、便捷地管理这些图片&#xff0c;却成为了一个令人头疼的问题。 第一步&#xff0c;进入首助编辑高手主页面&#xff0c;在上方…

Vagrant + docker搭建Jenkins 部署环境

有人问&#xff0c;为什么要用Jenkins&#xff1f;我说下我以前开发的痛点&#xff0c;在一些中小型企业&#xff0c;每次开发一个项目完成后&#xff0c;需要打包部署&#xff0c;可能没有专门的运维人员&#xff0c;只能开发人员去把项目打成一个war包&#xff0c;可能这个项…

(动画详解)LeetCode面试题 02.04.分割链表

&#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到动画详解LeetCode系列 用通俗易懂的动画的动画使leetcode算法题可视化 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读…

3D 生成重建010-SyncDreamer从单视图生成一致性的多视图

3D 生成重建010-SyncDreamer从单视图生成一致性的多视图 文章目录 0论文工作1论文方法2 效果 0论文工作 在zero123中&#xff0c;首先探索了给2d图像扩散模型注3d空间感知能力。可以将原图输入模型&#xff0c;通过相机位置的相对偏移生成对应的新视图。 这篇论文就是在zero1…

PAD如何实现在用RJ45上网的同时还能保证PAD的续航?|边充电边上网

在数字化时代&#xff0c;手机已经成为我们生活、工作的得力助手。当提及手机边上网边充电时&#xff0c;或许您会想&#xff1a;“这不是常态吗&#xff1f;”但今天&#xff0c;我们要探讨的是一个更为特殊而重要的场景——有线网络直连手机。对于那些需要稳定网络连接、不能…

51输出周期为40ms的方波(C+汇编)

题目 已知Fosc12MHz&#xff0c;T1工作于方式1&#xff0c; ①&#xff1a;实现20ms延时&#xff0c;求定时器初值TH0&#xff1f;TL0&#xff1f;写出具体的计算过程。 ②&#xff1a;利用汇编或C语言编程实现输出周期为40ms的方波。 周期为40ms的方波&#xff0c;半周期就…

weblogic 反序列化 CVE-2018-2628

这个漏洞因为java版本问题一直下载不了ysoserial反序列化工具&#xff0c;没办法生成payload。这里记录一下漏洞原理。 一、漏洞简介 Weblogic Server中的RMI 通信使用T3协议在Weblogic Server和其它Java程序&#xff08;客户端或者其它Weblogic Server实例&#xff09;之间传…

四川景源畅信:小白做抖音电商怎么样?

在数字时代&#xff0c;抖音已成为一个不可忽视的电商平台。对于初入行的小白来说&#xff0c;涉足抖音电商似乎既充满机遇又伴随着挑战。要判断小白做抖音电商的可行性&#xff0c;我们不妨从几个关键方面进行深入探讨。 一、市场趋势与流量获取 抖音作为新媒体的代表之一&…

2-1 EXTI外部中断(gd32)

中断的概念 中断硬件结构/软件结构 EXTI中断 EXTI硬件结构 注&#xff1a;EXTI线在同一时刻只能连接一个GPIO口&#xff0c;如果我们先连接了PA0,然后又连接了PB0那么此时PA0这个IO口就失去作用。 中断触发函数 中断优先级 中断优先级 数值越小优先级越高&#xff0c;抢占优先级…

[AutoSar]BSW_Diagnostic_004 ReadDataByIdentifier(0x22)的配置和实现

目录 关键词平台说明背景一、配置DcmDspDataInfos二、配置DcmDspDatas三、创建DcmDspDidInfos四、创建DcmDspDids五、总览六、创建一个ASWC七、mapping DCM port八、打开davinci developer&#xff0c;创建runnabl九、生成代码 关键词 嵌入式、C语言、autosar、OS、BSW、UDS、…

【HCIP学习】BGP选路、过滤及属性

一、BGP路由选路原则&#xff08;13条&#xff09; 1、首先丢弃下一跳&#xff08;NEXT_HOP&#xff09;不可达的路由&#xff1b; 2、优选Preferred-value值最大的路由&#xff1b;默认为0&#xff1b; Preferred-value&#xff1a;定义&#xff1a;首选项。 属性值&#…

5. 简单说一说uniapp中的语法吧

前言 如果你 知道Vue3并且对Vue3的语法有一定了解&#xff0c;请跳过这一章&#xff0c;由于后续项目主要是基于Vue3TypeScript&#xff0c;因此提前简单概述一些Vue3的基础语法~ 本文的目的是 期望通过对本文的阅读后能对Vue3的每个语法有一个简单的印象&#xff0c;至少要知…

Android 13 系统自定义安全水印

效果 源码实现 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java public final void showSafeModeOverlay() {View v LayoutInflater.from(mContext).inflate(com.android.internal.R.layout.safe_mode, null);WindowManager.Layout…

《C++学习笔记---初阶篇6》---string类 上

目录 1. 为什么要学习string类 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 2.2.1. string类对象的常见构造 2.2.2. string类对象的容量操作 2.2.3.再次探讨reserve与resize 2.2.4.string类对象的访问及遍历操作 2.2.5…

宝塔面板怎么解决nginx跨域问题

1.找到宝塔的nginx配置文件 宝塔有一点不同&#xff0c;nginx配置文件不在nginx的安装目录中&#xff0c;应当去/www/server/panel/vhost/nginx找到 2.添加你要跨域的地址 location /api {proxy_pass http://localhost:8080;proxy_set_header Host $host;proxy_set_header X-…