【C++】STL——string的模拟实现、常用构造函数、迭代器、运算符重载、扩容函数、增删查改

news2024/11/23 12:01:15

文章目录

  • 1.模拟实现string
    • 1.1构造函数
    • 1.2迭代器
    • 1.3运算符重载
    • 1.4扩容函数
    • 1.5增删查改

1.模拟实现string

string使用文章
在这里插入图片描述

1.1构造函数

  这里我们实现常用的第四个string(const char* s)和析构函数

在这里插入图片描述
在这里插入图片描述

class string {
public:
	//初始化列表赋值
	//string(const char* str = "\0")
	//	:_size(strlen(str))
	//	,_capacity(_size)
	//	,_str(new char[_capacity+1])
	//{
	//	memcpy(_str, str, _size + 1);
	//}
	
	//函数体内赋值
    string(const char* str = "\0") 
    {
        _size = strlen(str); // 获取字符串长度
        _capacity = _size; // 设置容量为长度
        _str = new char[_capacity + 1]; // 分配内存空间
        memcpy(_str, str, _size + 1); // 复制字符串内容
       	//strcpy(_str, str);
       	//这里最好不要使用strcpy,因为strcpy是C语言中的函数
       	//在检测到/0直接结束,若字符串中有/0,会导致部分字符串无法复制
    }

	~string()
	{
		_size = 0;
		_capacity = 0;
		delete[] _str;
		_str = nullptr;
	}

private:
    int _size; // 字符串长度
    int _capacity; // 字符串容量
    char* _str; // 字符串内容
};


  拷贝构造函数实现:

在这里插入图片描述

  在堆上使用new关键字为当前对象的成员变量_str分配内存空间,大小为s._capacity + 1字节,即字符串的容量加上一个结束符\0的空间。

  我们使用深拷贝而不是浅拷贝,浅拷贝会导致两个对象的指针指向同一块内存空间,当其中一个对象被析构时,会释放内存空间,导致另一个对象指向的内存变成悬空指针。 因此,在复制构造函数中使用了深拷贝来确保每个对象都有独立的内存空间存储字符串内容,避免出现悬空指针和程序崩溃的问题。

string(const string& s)
{
	this->_size = s._size;
	this->_capacity = s._capacity;
	
	//浅拷贝,两个指针指向同一块空间,且调用两次析构函数,程序崩溃
	//this->_str = s._str;

	//深拷贝 实现
	this->_str = new char[s._capacity + 1];
	//strcpy(_str, s.c_str());
	memcpy(_str, s.c_str(), _size + 1);
}

1.2迭代器

在这里插入图片描述
在这里插入图片描述

  我们通过迭代器来访问字符串的每个字符。begin 函数返回指向字符串的起始位置的迭代器,end 函数返回指向字符串的结束位置的迭代器。这样,就可以使用标准的迭代器操作来遍历字符串,如使用循环来遍历每个字符。

  对于普通的string类型,编译器调用上面两个类型iterator的迭代器,表示可以对该字符串可读可写;而对于const类型的string,编译器就会调用下面两个类型const_iterator的迭代器,表示对该字符串只有可写权限。

//实现迭代器和const迭代器
typedef char* iterator;
typedef const char* const_iterator;

iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

const_iterator begin() const
{
	return _str;
}

const_iterator end() const
{
	return _str + _size;
}

1.3运算符重载

operator[]

在这里插入图片描述
  和上面的迭代器实现类似,我们也需要实现两个operator[]。

  重载的普通版本的 operator[] 函数接受一个 size_t 类型的位置参数 pos。它首先使用 assert 断言来检查指定的位置是否位于有效的范围内,即小于字符串的长度 _size。然后,它返回 _str[pos],也就是字符串中指定位置 pos 处的字符的引用。因为该函数返回的是一个引用,所以可以通过该引用对字符进行修改。

  重载的const版本的 operator[] 函数与版本类似,但是函数本身被声明为const成员函数,以确保不会修改对象的成员变量。这意味着对const对象时operator[]只能进行读的权限,但不能通过返回的引用来修改字符。

  通过重载 [] 操作符,可以像操作数组一样方便地访问类中的字符。例如,对于字符串对象 str,可以使用 str[0] 来访问第一个字符,str[1] 来访问第二个字符,以此类推。

//重载[]操作符
char& operator[](size_t pos)
{
	assert(pos < _size);

	return _str[pos];
}
 
//重载const[]操作符
const char& operator[](size_t pos) const
{
	assert(pos < _size);

	return _str[pos];
}

赋值运算符

在这里插入图片描述

在这里插入图片描述

  传统写法中的赋值操作符重载函数首先进行自我赋值的判断,以确保在自我赋值的情况下不会出现问题。 然后,创建一个临时的字符数组 tmp,大小为 s._capacity + 1,并使用 memcpy 函数将 s._str 的内容复制到 tmp 中。接下来,删除当前对象的 _str 所指向的内存空间,并将其指针指向新分配的 tmp。最后,更新当前对象的 _size 和 _capacity。

  现代写法中的赋值操作符重载函数使用了拷贝构造函数来简化实现。 它首先创建一个临时的字符串对象 tmp,并使用参数 s 初始化 tmp。然后,调用 swap 函数来交换当前对象的成员变量和 tmp 对象的成员变量。这样做的好处是,通过交换指针,可以避免进行手动的内存分配和释放,并且保证异常安全性。最后,返回当前对象的引用。

//传统写法
/*string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		memcpy(tmp, s._str, s._size + 1);
		delete[] _str;
		_str = tmp;

		_size = s._size;
		_capacity = s._capacity;
	}

	return *this;
}*/

//现代写法
void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

string& operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s);

		//this->swap(tmp);
		swap(tmp);
	}

	return *this;
}

//更简单的写法
//string& operator=(string tmp)
//{
//	swap(tmp);
//
//	return *this;
//}

其他用于string比较的运算符重载:

bool operator<(const string& s) const
{
	int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
	return ret == 0 ? _size < s._size : ret < 0;
}

bool operator==(const string& s) const
{
	return _size == s._size
	&& memcmp(_str, s._str, _size) == 0;
}

bool operator<=(const string& s) const
{
	return *this < s || *this == s;
}

bool operator>(const string& s) const
{
	return !(*this <= s);
}

bool operator>=(const string& s) const
{
	return !(*this < s);
}

bool operator!=(const string& s) const
{
	return !(*this == s);
}

1.4扩容函数

reserve和resize

在这里插入图片描述
在这里插入图片描述

  这两个函数的作用是在需要扩容或调整字符串大小的情况下,确保字符串对象具有足够的容量和正确的大小。其中,reserve 函数用于扩容容量,resize 函数用于调整大小,并在必要时进行内存分配和字符填充。

//扩容capacity
void reserve(size_t n)
{
	if (n >= _capacity)
	{
		char* tmp = new char[n + 1];
		//strcpy(tmp,_str);
		memcpy(tmp, _str, _size + 1);

		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

//扩容size
void resize(size_t n, char ch = '\0')
{
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		reserve(n);

		for (size_t i = _size; i < n; i++)
		{
			_str[i] = ch;
		}

		_size = n;
		_str[_size] = '\0';
	}
}

1.5增删查改

push_back和append和+=运算符重载

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  这些函数的作用是向字符串对象中添加字符或字符数组。其中,push_back 可以用于逐个添加字符到字符串末尾,append 可以将字符数组追加到字符串中,operator+= 运算符重载则提供了一种简便的方式对字符串和字符进行连接。

//实现尾插
void push_back(const char ch)
{
	//先扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	this->_str[_size] = ch;

	++_size;
	this->_str[_size] = '\0';
}

//实现append
void append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		//至少要扩容到_size+len
		reserve(_size + len);
	}

	//strcpy(_str + _size, str);
	memcpy(_str + _size, str, len + 1);
	_size += len;
}

//复用实现+=的运算符重载
string& operator+=(const char ch)
{
	push_back(ch);
	return *this;
}


完整实现(包含insert、erase、find、substr、operator<<、operator>>)

#include<assert.h>

namespace str
{
	class string
	{
	public:
		//实现迭代器和const迭代器
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}

		构造函数为空值
		//string()
		//	:_size(0)
		//	, _capacity(_size)
		//	, _str(new char[1])
		//{
		//	_str[0] = '\0';
		//}

		构造函数传字符串
		//string(const char* str)
		//	:_size(strlen(str))
		//	,_capacity(_size)
		//	,_str(new char[_capacity+1])
		//{
		//	memcpy(_str, str, _size + 1);
		//}

		//合并string()和string(const char* str)
		//并且在函数体内赋值
		string(const char* str = "\0")//->"\0"\0
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			//为空时,实参未显示赋值,我们给str缺省参数"\0"
			//str传给_str"\0",系统设为空
			//"\0"为字符串以指针传递、'\0'为单个字符以单个字符传递
			//strcpy(_str, str);
			memcpy(_str, str, _size + 1);
		}

		string(const string& s)
		{
			this->_size = s._size;
			this->_capacity = s._capacity;
			
			//浅拷贝,两个指针指向同一块空间,且调用两次析构函数,程序崩溃
			//this->_str = s._str;

			//深拷贝 实现
			this->_str = new char[s._capacity + 1];
			//strcpy(_str, s.c_str());
			memcpy(_str, s.c_str(), _size + 1);
		}

		//传统写法
		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				memcpy(tmp, s._str, s._size + 1);
				delete[] _str;
				_str = tmp;

				_size = s._size;
				_capacity = s._capacity;
			}

			return *this;
		}*/

		//现代写法
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);
		//
		//		//this->swap(tmp);
		//		swap(tmp);
		//	}
		//
		//	return *this;
		//}

		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

		//析构函数
		~string()
		{
			_size = 0;
			_capacity = 0;
			delete[] _str;
			_str = nullptr;
		}

		//const返回c类型的字符串
		const char* c_str() const
		{
			return _str;
		}

		//const返回str的大小
		size_t size() const
		{
			return _size;
		}

		//重载[]操作符
		char& operator[](size_t pos)
		{
			assert(pos < _size);

			return _str[pos];
		}
		 
		//重载const[]操作符
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);

			return _str[pos];
		}

		//扩容函数capacity
		void reserve(size_t n)
		{
			if (n >= _capacity)
			{
				char* tmp = new char[n + 1];
				//strcpy(tmp,_str);
				memcpy(tmp, _str, _size + 1);

				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

		//扩容size
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				reserve(n);

				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}

				_size = n;
				_str[_size] = '\0';
			}
		}

		//实现尾插
		void push_back(const char ch)
		{
			//先扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			this->_str[_size] = ch;

			++_size;
			this->_str[_size] = '\0';
		}

		//实现append
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				//至少要扩容到_size+len
				reserve(_size + len);
			}

			//strcpy(_str + _size, str);
			memcpy(_str + _size, str, len + 1);
			_size += len;
		}

		//复用实现+=的运算符重载
		string& operator+=(const char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		void insert(size_t pos, size_t n, char ch)
		{
			assert(pos <= _size);

			if (_size + n > _capacity)
			{
				reserve(_size + n);
			}

			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + n] = _str[end];
				--end;
			}

			for (size_t i = 0; i < n; i++)
			{
				_str[pos + i] = ch;
			}

			_size += n;
		}

		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				// 至少扩容到_size + len
				reserve(_size + len);
			}

			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			for (size_t i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];
			}

			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos <= _size);

			if (len == npos || pos + len >= _size)
			{
				//_str[pos] = '\0';
				_size = pos;

				_str[_size] = '\0';
			}
			else
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}

		size_t find(char ch, size_t pos = 0)
		{
			assert(pos < _size);

			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < _size);

			const char* ptr = strstr(_str + pos, str);
			if (ptr)
			{
				return ptr - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);

			size_t n = len;
			if (len == npos || pos + len > _size)
			{
				n = _size - pos;
			}

			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; i++)
			{
				tmp += _str[i];
			}

			return tmp;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

		//bool operator<(const string& s)
		//{
		//	size_t i1 = 0;
		//	size_t i2 = 0;
		//	while (i1 < _size && i2 < s._size)
		//	{
		//		if (_str[i1] < s._str[i2])
		//		{
		//			return true;
		//		}
		//		else if (_str[i1] > s._str[i2])
		//		{
		//			return false;
		//		}
		//		else
		//		{
		//			++i1;
		//			++i2;
		//		}
		//	}

		//	/*if (i1 == _size && i2 != s._size)
		//	{
		//		return true;
		//	}
		//	else
		//	{
		//		return false;
		//	}*/

		//	//return i1 == _size && i2 != s._size;
		//	return _size < s._size;
		//}

		bool operator<(const string& s) const
		{
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
			return ret == 0 ? _size < s._size : ret < 0;
		}

		bool operator==(const string& s) const
		{
			return _size == s._size
				&& memcmp(_str, s._str, _size) == 0;
		}

		bool operator<=(const string& s) const
		{
			return *this < s || *this == s;
		}

		bool operator>(const string& s) const
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s) const
		{
			return !(*this < s);
		}

		bool operator!=(const string& s) const
		{
			return !(*this == s);
		}

	private:
		int _size;
		int _capacity;
		char* _str;

	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, str::string& s)
	{
		//for (size_t i = 0; i < s.size(); i++)
		//{
		//	out << s[i];
		//}

		for (auto ch : s)
		{
			out << ch;
		}

		return out;
	}

	istream& operator>>(istream& in, std::string& s)
	{
		s.clear();

		char ch = in.get();
		while (ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}

		//in >> ch;
		char buff[128];
		int i = 0;

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//in >> ch;
			ch = in.get();
		}

		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
};

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

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

相关文章

如何搭建一个成功的书店小程序

随着互联网的快速发展&#xff0c;传统实体书店面临着客流量下降的困境。为了适应市场需求&#xff0c;书店行业转型变得尤为重要。而制作书店小程序商城则成为了一种必备的转型方式。下面将详细介绍如何利用专业的小程序商城制作平台&#xff0c;手把手制作书店小程序商城。 首…

Docker 基础知识解析:容器与虚拟化的区别与优势

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

DTH11 温湿度模块

文章目录 前言一、DTH11 模块介绍二、设备树设置三、驱动程序四、测试程序五、上机测试及效果总结 前言 DHT11 是一款可测量 温度 和 湿度 的传感器。比如市面上一些空气加湿器&#xff0c;会测量空气中湿度&#xff0c;再根据测量结果决定是否继续加湿。 一、DTH11 模块介绍 …

VS下c++解析pcap文件

一、pcap文件格式 https://www.cnblogs.com/Chary/articles/15716063.html 接口协议&#xff08;四&#xff09;&#xff1a;以太网&#xff08;Ethernet&#xff09;学习&#xff08;一&#xff09;&#xff1a;协议_以太网协议_QNee的博客-CSDN博客 二、代码 pcapParser.h #…

向npm注册中心发布包(下)

目录 1、在package.json文件中指定dependencies和devDependencies 1.1 将依赖项添加到 package.json 文件 1.2 从命令行中 将依赖项添加到 package.json 文件 1.3 手动编辑 package.json 文件 2、关于语义版本控制 2.1 在已发布的包中增加语义版本 2.2 使用语义版本控制…

CentOS7系统Nvidia Docker容器基于TensorFlow2.12测试GPU

CentOS7系统Nvidia Docker容器基于TensorFlow1.15测试GPU 参考我的另一篇博客 1. 安装NVIDIA-Docker的Tensorflow2.12.0版本 1. 版本依赖对应关系&#xff1a;从源代码构建 | TensorFlow GPU 版本Python 版本编译器构建工具cuDNNCUDAtensorflow-2.6.03.6-3.9GCC 7.3.1Ba…

Linux设置密码复杂度

在etc目录下pam.d目录下&#xff0c;存在system-auth文件 先将文件备份下&#xff0c;然后在system-auth中插入下面行 password requisite pam_pwquality.so try_first_pass local_users_only retry3 authtok_type minlen8 lcredit-1 ucredit-1 dcredit-1 ocredi…

OpenCV 4.0+Python机器学习与计算机视觉实战

&#x1f482; 个人网站:【办公神器】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言第一部分&…

Shell脚本学习-变量子串

变量子串&#xff1a; man bash&#xff0c;然后搜索&#xff1a;Parameter Expansion。 参数拓展 $字符引进、提出了参数拓展、命令替换和数字替换。变量名或标识被大括号包围才能够被拓展。 我们可以记住一个表&#xff1a; 序号表达式说明1${parameter}返回变量$paramete…

东南大学齿轮箱故障诊断(Python代码,CNN结合LSTM模型)

运行代码要求&#xff1a; 代码运行环境要求&#xff1a;Keras版本>2.4.0&#xff0c;python版本>3.6.0 1.东南大学采集数据平台&#xff1a; 数据 该数据集包含2个子数据集&#xff0c;包括轴承数据和齿轮数据&#xff0c;这两个子数据集都是在传动系动力学模拟器&am…

PVS-Studio Crack,重新编译后的自动分析

PVS-Studio Crack,重新编译后的自动分析 PVS Studio执行静态代码分析并生成报告&#xff0c;帮助程序员查找和修复错误。PVS Studio执行广泛的代码检查&#xff0c;搜索印刷错误和复制粘贴错误也很有用。此类错误的示例&#xff1a;V501、V517、V522、V523、V3001。 静态分析的…

常常会用到的截取字符串substr()、substring()、slice()方法详解

常常会用到的截取字符串substr()、substring()、slice()方法详解 slice() 定义&#xff1a;接受一个或者两个参数&#xff0c;第一个参数指定子字符串的开始位置。第二个参数表示子字符串的结束位置&#xff08;不包括结束位置的那个字符&#xff09;&#xff0c;如果没有传递…

Linux用户权限问题详解

Linux用户权限问题详解 【一】Linux权限的概念&#xff08;1&#xff09;用户类型&#xff08;2&#xff09;如何切换用户&#xff08;3&#xff09;用户相关的一些命令 【二】Linux文件权限管理&#xff08;1&#xff09;文件访问者的分类&#xff08;2&#xff09;文件类型和…

激光点云数据如何在客户端进行管理、查看及分享?

四维轻云是一款地理空间数据在线管理平台&#xff0c;具有地理空间数据的在线管理、查看及分享等功能。在四维轻云平台中&#xff0c;用户可以不受时间地点限制&#xff0c;随时随地上传数字高程模型、激光点云、倾斜摄影模型、正射影像等地理空间数据。 现在&#xff0c;小编…

算法(3)

喝汽水 两瓶即可换一瓶 import java.util.*; public class Main {public static void main(String[] args) {//剩2个空瓶子时&#xff0c;可以先找老板借一瓶汽水&#xff0c;喝掉这瓶满的&#xff0c;喝完以后用3个空瓶子换一瓶满的还给老板。 //也就是说2个空瓶子即可换一瓶汽…

vue如何设置网站标题和logo图标

目录 1、在根目录找到项目index.html文件 2、在index.html 的 title标签中修改名称为自己设计的标题 3、在index.html 的 title标签下的link标签中引入图标 ①格式为&#xff1a; ②注意&#xff1a; 1、在根目录找到项目index.html文件 2、在index.html 的 title标签中修改…

Visual Assist X Crack

Visual Assist X Crack Visual Assist X通过Visual Studio中的关键新功能和对现有功能的改进&#xff0c;大大缩短了应用程序开发时间&#xff0c;使您能够&#xff1a;Visual Assist提高了自动化程度&#xff0c;简化了导航&#xff0c;并在开发过程中显示重要信息。这些功能已…

WebDAV之π-Disk派盘+ WinSCP

WinSCP是一个免费的开源文件传输应用程序&#xff0c;它使用文件传输协议&#xff0c;安全外壳文件传输协议和安全复制协议来进行纯文件或安全文件传输。该应用程序旨在与Windows一起使用&#xff0c;并支持常见的Windows桌面功能&#xff0c;例如拖放文件&#xff0c;跳转列表…

Python web实战 | 使用 Django 搭建 Web 应用程序 【干货】

概要 从社交媒体到在线购物&#xff0c;从在线银行到在线医疗&#xff0c;Web 应用程序为人们提供了方便快捷的服务。Web 应用程序已经成为了人们日常生活中不可或缺的一部分。搭建一个高效、稳定、易用的 Web 应用程序并不是一件容易的事情。本文将介绍如何使用 Django 快速搭…

2_Apollo4BlueLite中断控制器NVIC

1.概述 Apollo4BlueLite 的中断控制器是采用 ARM Cortex-M4 内核&#xff0c;并集成了 NVIC&#xff08;Nested Vectored Interrupt Controller&#xff0c;嵌套向量中断控制器&#xff09;作为其中断控制器。 NVIC 是 ARM Cortex-M 系列处理器中常用的中断控制器&#xff0c…