【C++】————string基础用法及部分函数底层实现

 9efbcbc3d25747719da38c01b3fa9b4f.gif

                                                      作者主页:     作者主页

                                                      本篇博客专栏:C++

                                                      创作时间 :2024年6月30日

9efbcbc3d25747719da38c01b3fa9b4f.gif

前言:

 本文主要介绍STL容器之一  ----  string,在学习C++的过程中,我们要将C++视为一个语言联邦(摘录于Effective C++ 条款一)。如何理解这句话呢,我们学习C++,可将其分为四个板块;分别为C、Object-Oriented C++(面向对象的C++)、Template C++(模板)、STL。本文就介绍STL中的string;

一、string是什么?

  string是STL文档的容器之一,是一个自定义类型,是一个类,由类模板basic_string实例化出来的一个类;

我们看一下cplusplus上是咋介绍的?

我们简单看一下即可,下面我来为大家做介绍。

二、string的使用:

  由于string出现的时间实际是早于STL的,是后来划分进STL库的,所以string开始的设计比较冗余,有许多没有必要的接口(一共106个接口函数);这也是被广大C++程序员吐槽的一个槽点,我们无需将每一个接口都记住,我们需要将核心接口记住并熟练使用,遇见一些默认的接口查看文档即可;

2.1构造函数

        在C++98中,string的构造函数一种有如下7种;

int main()
{
	// 1、无参默认构造
	// string();
	string s1;
	// 2、拷贝构造   
	// string (const string& str);
	string s2(s1);
	// 4、通过字符串常量初始化   
	// string (const char* s);
	string s4("hello world");
	// 3、通过字符串子串初始化   
	// string (const string& str, size_t pos, size_t len = npos);
	string s3(s4, 5, 5);
	// 5、通过字符串前n个字符初始化   
	// string (const char* s, size_t n);
	string s5("hello wrold", 6);
	// 6、用n个字符c初始化字符串  
	//  string (size_t n, char c);
	string s6(10, 'x');
	// 7、迭代器区间初始化(暂不介绍)
	return 0;
}

 其中提一下第三种,pos为子串的位置,len子串的长度,若len大于从子串pos位置开始后面字符总数,则表示初始化到子串结尾即可,比如我们要用 “hello world” 初始化字符串,若pos为6,len为20,则用world初始化字符串s1;len还有一个缺省值npos,其数值为无符号整型的-1,也就是无符号的最大值(无符号无负数);

2.2赋值重载:

赋值重载使string能够用=对string对象重新赋值,string的赋值重载一共有有如下三种;

int main()
{
	string tmp("hello world");
	string s1;
	string s2;
	string s3;
	// 1、string类进行赋值重载
	s1 = tmp;
	// 2、使用字符串常量赋值重载
	s2 = "hello world";
	// 3、使用字符赋值重载
	s3 = 'A';
    return 0;
}

2.3容量相关接口

 首先介绍如下六个简单一些的接口;

int main()
{
	string s1("hello world");
	// string中储存的字符个数(不包括\0)
	cout << s1.length() << endl;
	// 与length功能相同
	cout << s1.size() << endl;
	// 可以最多储存多少个字符(理论值,实际上并没有那么多)
	cout << s1.max_size() << endl;
	// string的当前容量
	cout << s1.capacity() << endl;
	// 当前string对象是否为空
	cout << s1.empty() << endl;
	// 清空s1中所有字符
	s1.clear();
    return 0;
}

 其中的length与size两种并无相异,由于string出现的较早,当时没有STL其他容器,先出现了length,后来为了统一接口,于其他容器接口保持一致,因此出现了size;

int main()
{
	// reserve 提前开空间(可能会大于指定的大小,因此开空间规则不同)
	string s1;
	s1.reserve(100);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	// resize 提前开空间并初始化 缺省值为0
	string s2;
	s2.resize(100);
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;
	s2 += "hhhhhh";
    return 0;
}

  我们发现reserve仅仅只是修改capacity,而resize不仅会修改capacity,还会修改size,然后用第二个参数取去初始化新增的区间;当指定大小小于原来空间时,reserve什么都不会做,而resize则会则断大于指定大小后面的区域(在后面补零);

总结:reserve仅改变capacity,resize既改变capacity又改变size;当指定大小小于字符串的size时,resize还可以截断(在后面补 \0 )

2.4迭代器

    迭代器是STL库中的一个特殊的存在,我们可以通过迭代器对string类中的字符进行增删查改; 在string类中,我们可将其视为指针;string类中的迭代器接口有如下几种;

 begin函数返回的是字符串中第一个字符的位置的迭代器,而end函数返回的字符串中最后一个字符的下一个位置的迭代器; 因此遍历一个string类,有一下三种方法;

int main()
{
	string s1("hello world");
	// 三种遍历方式
	// 1、通过[]来访问每一个字符
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	// 2、通过迭代来来访问每一个字符
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	// 3、通过范围for(其实范围for就是编译器替换成了迭代器遍历的方法)
	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;
    return 0;
}

     rbegin与rend系列为反向迭代器;rbegin返回的是最后一个字符的位置的迭代器,rend返回的是第一个字符的前一个位置的迭代器;

 我们可以通过反向迭代器,对其逆向遍历;反向迭代器的类型为 string::reverse_iterator;

int main()
{	
    string s1("hello world");
    string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << " ";
		rit++;
	}
 
}

2.5下标访问:

    关于元素的访问,也有如下四个接口,最常用的还是方括号; 

int main()
{
	// []重载使string可以像字符数组一样访问
	string s1("hello world");
	cout << s1[0] << endl;
	cout << s1[1] << endl;
	// at 于[] 功能相同,只不过[]的越界是由assert来限制,而at则是抛异常
	cout << s1.at(0) << endl;
	cout << s1.at(1) << endl;
	// front访问string中第一个字符
	cout << s1.front() << endl;
	// back访问string中最后一个字符
	cout << s1.back() << endl;
    return 0;
}

方括号的使用如同数组的方括号使用相同;at与方括号用法相同,只是遇见非法访问时是抛异常解决;

2.6修改

string的修改接口设计得十分冗余;其中我们可以用+=替代append与push_back;实际中,也是+=用得比较多,但是我们还是了解一下相关用法; 

  +=我们可以加等一个string类,可以加等一个字符,也可以加等一个字符指针;因此有以下用法;

int main()
{
	string tmp("xxxx");
	string s1("hello world");
	// += 字符
	s1 += ' ';
	// += string类
	s1 += tmp;
	// += 字符指针
	s1 += " hello world";
	cout << s1 << endl;
}

int main()
{
	string tmp("xxxx");
	string s1;
	// 尾加字符
	// void push_back (char c);
	s1.push_back('c');
	// 尾加string类  
	// string& append (const string& str);
	s1.append(tmp);
	// 尾加string从subpos位置开始的sublen个字符   
	//string& append (const string& str, size_t subpos, size_t sublen);
	s1.append(tmp, 2, 3);
	// 用字符指针指向的字符串/字符尾加
	// string& append (const char* s);
	s1.append("hello world");
	// 用字符指针指向的字符串的前n个字符尾加
	// string& append (const char* s, size_t n);
	s1.append("hello world", 6);
	// 尾加n个c字符   
	// string& append (size_t n, char c);
	s1.append(5, 'x');
	// 迭代器区间追加
	// template <class InputIterator>
	// string& append(InputIterator first, InputIterator last);
    s1.append(tmp.begin(), tmp.end());
	cout << s1 << endl;
    return 0;
}

 assign为string的赋值函数;是一个扩增版的operator =,用的并不多,主要用法如下;

int main()
{
	string tmp("hello world");
	string s1;
	// 使用string类对其赋值
	// string& assign (const string& str);
	s1.assign(tmp);
	cout << s1 << endl;
	// 使用string类中从subpos位置开始的sublen个串来赋值
	// string& assign (const string& str, size_t subpos, size_t sublen);
	s1.assign(tmp, 2, 5);
	cout << s1 << endl;
	// 使用字符指针所指向的字符串对其赋值
	// string& assign (const char* s);
	s1.assign("hello naiths");
	cout << s1 << endl;
	// 使用字符指针所指向的字符串的前n个对其赋值
	// string& assign (const char* s, size_t n);
	s1.assign("hello naiths", 7);
	cout << s1 << endl;
	// 使用n个c字符对其赋值
	// string& assign (size_t n, char c);
	s1.assign(10, 'x');
	cout << s1 << endl;
	// 使用迭代器对其赋值
	// template <class InputIterator>
	// string& assign(InputIterator first, InputIterator last);
    s1.assign(tmp.begin(), tmp.end());
    cout << s1 << endl;
    return 0;
}

int main()
{
	string tmp("hello world");
	string s1;
	// 在pos位置插入string类字符串
	// string& insert (size_t pos, const string& str);
	s1.insert(0, tmp);
	cout << s1 << endl;
	// 在pos位置插入str的子串(subpos位置开始的sublen个字符)
	// string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
	s1.insert(7, tmp, 0, 6);
	cout << s1 << endl;
	// 在pos位置插入字符指针指向的字符串
	// string& insert (size_t pos, constchar* s);
	s1.insert(2, "xxx");
	cout << s1 << endl;
	// 在pos位置插入字符指针指向的字符串的前n个字符
	// string& insert (size_t pos, const char* s, size_t n);
	s1.insert(7, "hello naiths", 8);
	cout << s1 << endl;
	// 在pos位置插入n个c字符
	// string& insert (size_t pos, size_t n, char c);
	s1.insert(0, 5, 'y');
	cout << s1 << endl;
	// 指定迭代器的位置插入n个字符c
	// void insert (iterator p, size_t n, char c);
	string::iterator it = s1.begin() + 10;
	s1.insert(it, 10, 'z');
	cout << s1 << endl;
	// 指定迭代器的位置插入字符c
	// iterator insert (iterator p, char c);
	s1.insert(s1.begin(), 'A');
	cout << s1 << endl;
	// 指定p位置插入迭代器区间的字符
	// template <class InputIterator>
	// void insert(iterator p, InputIterator first, InputIterator last);
	s1.insert(s1.begin(), tmp.begin() + 3, tmp.begin() + 8);
	cout << s1 << endl;
	// 删除pos位置开始的len个字符
	// string& erase (size_t pos = 0, size_t len = npos);
	s1.erase(2, 5);
	cout << s1 << endl;
	// 删除迭代器位置的那个字符
	// iterator erase (iterator p);
	s1.erase(s1.begin());
	cout << s1 << endl;
	// 删除迭代器区间的字符
	// iterator erase (iterator first, iterator last);
	s1.erase(s1.begin() + 2, s1.begin() + 5);
	cout << s1 << endl;
    return 0;
}

三、string底层实现

其实对于上面的这些库里面的函数,我们不需要全去记住,记住一些常用的即可,其他的等到我们要用到的时候去cplusplus网站里面找即可,对于下面我们要自己实现的函数,一定要记住,这些函数用的都是比较多的。

#pragma once
#define _CRT_SECURE_NO_WARNINGS

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


namespace bit
{
	class string
	{
	public:
		//string();//不传参的情况
		string(const string& s);
		string(const char* str = "");//这里可以之际全缺省
		~string();
		const char* c_str() const;

		size_t size() const;
		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;
		string& operator=(const string& s);
		//实现一个迭代器
		typedef char* iterator;
		iterator begin();
		iterator end();

		typedef const char* const_iterator;
		const_iterator begin() const;
		const_iterator end() const;



		void reserve(size_t n);

		void push_back(char ch);
		void append(const char* str);

		string& operator+=(char ch);
		string& operator+=(const char* str);


		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos, size_t len=npos);
		size_t find(char ch, size_t pso = 0);
	    size_t find(const char* str, size_t pos = 0);
		void swap(string& s);
		string substr(size_t pos,size_t len);
		void clear();
	private:
		char* _str;
		size_t _size;
		size_t _capacity;


		const static size_t npos;
	};
	

	istream& operator>>(istream& is, string& str);
	ostream& operator<<(ostream& os, const string& str);

}


上面这些就是我们要实现的一些函数的底层是什么样的,当然,我只是基于自己的理解去写这些,比起C++库里面的肯定要low一点,所以我们理解基础的一些思路即可。

下面我们来看一下这些代码,大家看一下,想要更好的理解可以自己去实现一遍。

#include"string.h"




namespace bit
{
	//深拷贝
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	 const size_t string::npos = -1;
	//不传参的析构
	//string::string()
	/*{
		_str = new char[1] {'\0'};
		_size = 0;
		_capacity = 0;
	}*/

	//构造函数
	string::string(const char* str)
		//strlen是运行时计算长度,效率比较低,三个strlen重复计算了
		//但是如果像下面这样写,还是会出现一些问题,因为初始化列表的初始化是按声明的顺序初始化,这里就会出现问题
		/*:_str(new char[strlen(str) + 1])
		,_size(strlen(str))
		,_capacity(strlen(str))*/
		: _size(strlen(str))
		
	{
		_capacity = _size;
		_str = new char[_size + 1];
		strcpy(_str, str);
	}

	//赋值
	string& string::operator=(const string& s)
	{
		//避免自己给自己赋值
		if (this != &s)
		{
			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);
			//这样写是为了避免前面的空间大小小于后面的那个
			delete[] _str;
			_str = tmp;
			
		}
		return *this;
	}

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

	//打印
	const char* string::c_str() const//像这种用于打印的函数,可以在后面加上一个const,
		                             //加上const意味着不能修改指向的内容和本事的值,这样即使放生权限的缩小依然不会报错
	{
		return _str;
	}


	size_t string::size() const
	{
		return _size;
	}

	char& string::operator[](size_t pos)//可读可写
	{
		assert(pos < _size);
		return _str[pos];
	}

	const char& string::operator[](size_t pos) const
	{
		assert(pos < _size);
		return _str[pos];
	}


	//迭代器
	string::iterator string::begin()
	{
		return _str;
	}
	string::iterator string::end()
	{
		return _str + _size;
	}

	//无法修改的迭代
	string::const_iterator string::begin() const
	{
		return _str;
	}
	string::const_iterator string::end() const
	{
		return _str + _size;
	}


	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[]_str;

			_str = tmp;
			_capacity = n;
		}
		
	}

	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}
		_str[_size] = ch;
		_str[++_size] = '\0';
	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
            reserve(_size + len);
		}
		strcpy(_str + _size, str);
		_size += len;
	}
	

	string& string::operator+=(char ch)
	{
		push_back(ch);

		return *this;
	}

	string& string::operator+=(const char* str)
	{
		append(str);

		return *this;
	}

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

		size_t len = strlen(str);
		if (_size + len >= _capacity)
		{
			reserve(_size + len);
		}
		int end = _size;
		while (end >= pos)
		{
			_str[end+len] = _str[end];
			--end;
		}

		memcpy(_str + pos, str, len);
		_size += len;
		
	}

	void string::insert(size_t pos,char ch)
	{
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}
		
		size_t end = _size;
		while (end >= pos)
		{
			_str[end + 1] = _str[end];
			--end;
		}
		_str[pos] = ch;

	}

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

		//当len大于前面的个数
		if (len>=_size-pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}
	}

	size_t string::find(char ch, size_t pos)
	{
		
		while (pos!=_size)
		{
			if (ch == _str[pos])
			{
				return pos;
			}
			++pos;
		}
		return npos;
	}

    size_t string::find(const char* str, size_t pos)
	{
		char* p = strstr(_str + pos, str);
		return p - _str;
	}


	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_capacity, s._capacity);
		std::swap(_size, s._size);
	}

	string string::substr(size_t pos, size_t len)
	{
		//len大于后面的剩余字符,有多少取多少
		if (len > _size - pos)
		{
			string sub(_str - pos);
			return sub;
		}
		else
		{
			string sub;
			sub.reserve(len);
			for (size_t i = 0; i < len; i++)
			{
				sub += _str[pos + i];

			}
			return sub;
		}
	}

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

	istream& operator>>(istream& is, string& str)
	{
		char ch;
		is >> ch;
		while (ch != ' ' && ch != '\n')
		{
			str += ch;
			is >> ch;
		}

		return is;
	}

	ostream& operator<<(ostream& os, const string& str)
	{
		for (size_t i = 0; i < str.size(); i++)
		{
			os << str[i];
		}

		return os;
	}
	
}

	

最后:

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.一个冷知识:
屏蔽力是一个人最顶级的能力,任何消耗你的人和事,多看一眼都是你的不对。

2.你不用变得很外向,内向挺好的,但需要你发言的时候,一定要勇敢。
正所谓:君子可内敛不可懦弱,面不公可起而论之。

3.成年人的世界,只筛选,不教育。

4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。

5.你开始炫耀自己,往往都是灾难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

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

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

相关文章

读书笔记-《Spring技术内幕》(三)MVC与Web环境

前面我们学习了 Spring 最核心的 IoC 与 AOP 模块&#xff08;读书笔记-《Spring技术内幕》&#xff08;一&#xff09;IoC容器的实现、读书笔记-《Spring技术内幕》&#xff08;二&#xff09;AOP的实现&#xff09;&#xff0c;接下来继续学习 MVC&#xff0c;其同样也是经典…

朋友问我Java中“::”是什么意思?我汗流浃背了......

目录 一&#xff1a;什么是&#xff1a;&#xff1a;&#xff1f; 二&#xff1a;方法引用的几种类型 1.引用静态方法 2.引用特定对象的实例方法 3.引用特定类型的任意对象的实例方法 4.引用构造方法 三&#xff1a;方法引用的适用场景 四&#xff1a;总结 一&#xff1…

数学建模比赛介绍与写作建议

0 小序 本文的写作起因是导师要求我给打算参加相关竞赛的师弟们做一次讲座和汇报。我梳理了一个ppt提纲&#xff0c;并经过整理&#xff0c;因此有了这篇文章。 我打算从数学建模论文写作格式和写作技巧入手&#xff0c;接着介绍数学建模常用的数学模型&#xff0c;最后提出一…

sheng的学习笔记-AI-聚类(Clustering)

ai目录 sheng的学习笔记-AI目录-CSDN博客 基础知识 什么是聚类 在“无监督学习”(unsupervised learning)中&#xff0c;训练样本的标记信息是未知的&#xff0c;目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律&#xff0c;为进一步的数据分析提供基础。此类学…

【电源专题】为什么带电量计芯片的电池MOS保护要放在高侧

在实际的电量计电池开发中,发现一个很奇怪的现象。传统电池保护IC往往都是将充电保护和放电保护的两个MOS管放在低侧的。如下所示是文章:【电源专题】读一读单节锂电池保护IC规格书 可以看到M1和M2两个MOS管是放在PB-(也就是电池的负端),我们叫做低端。 而BQ28Z610电…

清华大学世界排名:2025QS世界大学排名第20名

近日&#xff0c;国际高等教育研究机构QS Quacquarelli Symonds正式发布了2025QS世界大学排名&#xff0c;其中麻省理工学院连续第13年蝉联榜首&#xff0c;北京大学排名由去年的全球第17上升至全球第14名&#xff0c;清华大学位列2025QS世界大学排名第20名&#xff0c;以下是查…

Linux——/etc/passwd文件含义,grep,cut

/etc/passwd文件含义 作用 - 记录用户账户信息&#xff1a;共分为7段&#xff0c;使用冒号分割 含义 - 文件内容意义&#xff1a;账户名&#xff1a;密码代号x&#xff1a;UID&#xff1a;GID&#xff1a;注释&#xff1a;家目录&#xff1a;SHELL - 第7列/sbin/nologin&#x…

大数据可视化实验(七):Python数据可视化

目录 一、实验目的... 1 二、实验环境... 1 三、实验内容... 1 1&#xff09;绘制带颜色的柱状图。.. 1 2&#xff09;绘制堆叠柱状图。.. 3 3&#xff09;绘制数学函数曲线图。.. 4 4&#xff09;使用seaborn绘制组合图形。... 5 5&#xff09;使用Boken绘制多个三角形…

软件框架(Framework)是什么?

可实例化的、部分完成的软件系统或子系统&#xff0c;它为一组系统或子系统定义了统一的体系结构(architecture)&#xff0c;并提供了构造系统的基本构造块(building blocks)&#xff0c;还为实现具体功能定义了扩展点(extending points)。 框架实现了体系结构级别的复用。 其…

深度学习评价指标:Precision, Recall, F1-score, mIOU, 和 mDice

在深度学习和机器学习中&#xff0c;评价模型性能是至关重要的一环。本文将详细讲解一些常见的评价指标&#xff0c;包括精确率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、F1-score、平均交并比&#xff08;mIOU&#xff09;和平均Dice系数&am…

[leetcode]beautiful-arrangement. 优美的排列

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:vector<vector<int>> match;vector<int> vis;int num;void backtrack(int index, int n) {if (index n 1) {num;return;}for (auto &x : match[index]) {if (!vis[x]) {vis[x] tru…

【C++】宏定义

严格来说&#xff0c;这个题目起名为C是不合适的&#xff0c;因为宏定义是C语言的遗留特性。CleanCode并不推荐C中使用宏定义。我当时还在公司做过宏定义为什么应该被取代的报告。但是适当使用宏定义对代码是有好处的。坏处也有一些。 无参宏定义 最常见的一种宏定义&#xf…

Python 面试【中级】

欢迎莅临我的博客 &#x1f49d;&#x1f49d;&#x1f49d;&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【游戏引擎之路】登神长阶(五)

5月20日-6月4日&#xff1a;攻克2D物理引擎。 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 6月13日-6月20日&#xff1a;攻克《3D图形教程》。 6月21日-6月22日&#xff1a;攻克《Raycasting游戏教程》。 6月23日-6月30日&#xff1a;攻克《Windows游戏编程大师技巧》。 …

厦门新能安科技Ampace校招实习待遇及Verify测评SHL演绎数字推理历年真题题库

一、厦门新能安科技公司介绍 厦门新能安科技有限公司主要业务包括电池制造和销售&#xff0c;电容器及其配套设备制造与销售&#xff0c;电池零配件生产与销售。此外&#xff0c;公司还提供包括技术服务、技术开发、技术咨询、技术交流、技术转让和技术推广在内的全方位服务。公…

安卓开发app-基础的java项目构建补充知识

安卓开发app-基础的java项目构建补充知识&#xff01;上一次分享了基础的项目构建&#xff0c;但是还遗漏了一些基础的内容。今天补充完整。 首先&#xff0c;是关于项目的一些配置文件的信息。 第一个配置文件&#xff1a;{setting.gradle} 国内阿里云仓库地址信息&#xff1…

深度学习基准模型Mamba

深度学习基准模型Mamba Mamba(英文直译&#xff1a;眼镜蛇)具有选择性状态空间的线性时间序列建模&#xff0c;是一种先进的状态空间模型 (SSM)&#xff0c;专为高效处理复杂的数据密集型序列而设计。 Mamba是一种深度学习基准模型&#xff0c;专为处理长序列数据而设计&…

WAIC2024 | 华院计算邀您共赴2024年世界人工智能大会,见证未来科技革新

在智能时代的浪潮汹涌澎湃之际&#xff0c;算法已成为推动社会进步的核心力量。作为中国认知智能技术的领军企业&#xff0c;华院计算在人工智能的广阔天地中&#xff0c;不断探索、创新&#xff0c;致力于将算法的潜力发挥到极致。在过去的时日里&#xff0c;华院计算不断探索…

界面控件Telerik UI for Winforms 2024 Q2新版亮点 - 发布全新的AI相关组件

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件&#xff0c;所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。 本文将介绍界面组件Telerik UI for Winform…

4个文章生成器免费版分享,让文章创作更轻松便捷

在当今这个信息飞速传播的时代&#xff0c;文章创作的重要性愈发凸显。无论是从事内容创作的专业人士&#xff0c;还是偶尔需要撰写文章的普通大众&#xff0c;都希望能更高效地完成文章创作任务。而在实际操作中&#xff0c;我们常常会遇到思路卡顿、没有创作灵感的问题。今天…