【c++】string类---标准库(STL)中的string类

主页:醋溜马桶圈-CSDN博客

专栏:c++_醋溜马桶圈的博客-CSDN博客

giteemnxcc (mnxcc) - Gitee.com

目录

1.STL(标准库)

1.1 什么是STL

1.2 STL的版本

1.3 STL的六大组件

1.4 STL的重要性

1.5 如何学习STL

6.STL的缺陷

2. 为什么要学习string类

2.1 C语言中的字符串

2.2 OJ中有关字符串的题目

3. 标准库中的string类

3.1 string类(了解)

3.2 string类的常用接口说明

1. string类对象的常见构造

2. string类对象的容量操作

3. string类对象的访问及遍历操作 

4. string类对象的修改操作

5. string类非成员函数

6. vs和g++下string结构的说明

1.vs下string的结构

2.g++下string的结构

4.string类的模拟实现 

4.1 浅拷贝

4.2 深拷贝

4.3 写时拷贝(了解)

4.4 string类模拟实现代码

1 代码内容和测试函数

2 传统版写法的String类

3 现代版写法的String类


1.STL(标准库)

1.1 什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架

1.2 STL的版本

  • 原始版本
  • Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。HP 版本--所有STL实现版本的始祖
  • P. J. 版本
  • 由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异
  • RW版本
  • 由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般
  • SGI版本
  • 由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高

1.3 STL的六大组件

1.4 STL的重要性

网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发

1.5 如何学习STL

简单总结一下 :学习 STL 的三个境界: 能用,明理,能扩展

6.STL的缺陷

  • STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。
  • STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
  • STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
  • STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的

2. 为什么要学习string

2.1 C语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问

2.2 OJ中有关字符串的题目

在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数

3. 标准库中的string

3.1 string(了解)

string类的文档介绍:https://cplusplus.com/reference/string/string/?kw=string

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作

总结:

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
  4. 不能操作多字节或者变长字符的序列

使用string类时,必须包含#include头文件以及using namespace std;

3.2 string类的常用接口说明

1. string类对象的常见构造

void Teststring()
{
	string s1; // 构造空的string类对象s1
	string s2("hello bit"); // 用C格式字符串构造string类对象s2
	string s3(s2); // 拷贝构造s3
}

2. string类对象的容量操作

注意:

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
  2. clear()只是将string中有效字符清空,不改变底层空间大小
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小

3. string类对象的访问及遍历操作 

4. string类对象的修改操作

注意:

  1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好

5. string类非成员函数

6. vs和g++下string结构的说明

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节

1.vs下string的结构

string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger one
	value_type _Buf[_BUF_SIZE];
	pointer _Ptr;
	char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高

其次:还有一个size_t字段保存字符串长度,一个

size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情

故总共占16+4+4+4=28个字节

2.g++下string的结构

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
  • 指向堆空间的指针,用来存储字符串
​struct _Rep_base
{
	size_type _M_length;
	size_type _M_capacity;
	_Atomic_word _M_refcount;
};

4.string类的模拟实现 

4.1 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了

4.2 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供

4.3 写时拷贝(了解)

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源

4.4 string类模拟实现代码

根据cplusplus网站中对string的介绍,我们可以手动实现相关的操作函数

string - C++ Reference (cplusplus.com)

作为一名c++的研发工程师,我们要对底层有更深的理解,学习大佬的写法,参考大佬的思维,这对我们以后学习和工作中是有很大帮助的

1 代码内容和测试函数

这是.h文件的内容

#pragma once
//#include <iostream>
#include <assert.h>
namespace dc
{
	class string
	{
	public:
		//指针版本
		typedef char* iterator;
		typedef const char* const_iterator;
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//String(char* str = nullptr)
		//	:_size(strlen(str))
		//{
		//	_capacity = _size;
		//	_str = new char[_capacity + 1];
		//	strcpy(_str, str);
		//}
		//String(const char* str = "\0")
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		string& operator=(const string& s)
		{
			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		const char* c_str() const
		{
			return _str;
		}
		size_t size() const
		{
			return _size;
		}
		size_t capacity() const 
		{
			return _capacity;
		}
		//const对象刻可读不可写
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}
		//普通对象可读可写
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		void resize(size_t n, char ch = '\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n+1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			扩容2倍
			//if (_size == _capacity)
			//{
			//	reserve(_capacity == 0 ? 4 : 2 * _capacity);
			//}
			//_str[_size] = ch;
			//++_size;
			//_str[_size] = '\0';
			insert(_size, ch);
		}
		void append(const char* str)
		{
			扩容
			//size_t len = strlen(str);
			//if (_size + len > _capacity)
			//{
			//	reserve(_size + len);
			//}
			//strcpy(_str + _size, str);
			//_size += len;
			insert(_size, str);
		}
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			//扩容2倍
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			//size_t end = _size;
			//while (end >= (int)pos)
			//{
			//	_str[end + 1] = _str[end];
			//	--end;
			//}
			//_str[pos] = ch;
			//_size++;
			size_t end = _size+1;
			while (end > pos)
			{
				_str[end] = _str[end-1];
				--end;
			}
			_str[pos] = ch;
			_size++;
		}
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			//扩容
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			//string tmp;
			//tmp.reserve(_size + len);
			//tmp += str;
			//tmp += _str;
			//strcpy(_str + pos, tmp.c_str());
			//_size += len;
			size_t end = _size + len;
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size+=len;
		}
		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || len >= _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		void swap(string& x, string& y)
		{
		
			x.swap(y);
		}
		size_t find(char ch, size_t pos = 0) 
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}
		size_t find(const char* sub, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}
		string substr(size_t pos = 0, size_t len = npos)
		{
			string sub;
			if (len == npos || len >= _size - pos)
			{
				for (size_t i = pos; i < _size; i++)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					sub += _str[i];
				}
			}
			return sub;
		}
		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
		//bool operator==(const string& s)
		//{
		//	int ret = strcmp(_str, s._str);
		//	return ret == 0;
		//}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		static const int npos;
	};
	const int string::npos = -1;
	//比较大小
	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}	
	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}
	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}
	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}
	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
 
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		//in >> ch;
		ch = in.get();
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			//s += ch;
			//in >> ch;
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
	istream& getline(istream& in, string& s)
	{
		s.clear();
		char ch;
		//in >> ch;
		ch = in.get();
		while (ch != '\n')
		{
			s += ch;
			//in >> ch;
			ch = in.get();
		}
		return in;
	}
	void test_string()
	{
		string s1;
		string s2("hello world");
		const string s3("111111");
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
		//String s1("hello world");
		//std::string s1("hello world");
		for (size_t i = 0; i < s2.size(); i++)
		{
			cout << s2[i] << " ";
		}
		cout << endl;
		//for (size_t i = 0; i < s2.size(); i++)
		//{
		//	s2[i]++;
		//}
		//cout << endl;
		for (size_t i = 0; i < s3.size(); i++)
		{
			cout << s3[i] << " ";
		}
		cout << endl;
	}
	void test_string2()
	{
		string s1("hello world");
		string::iterator it1 = s1.begin();
		while (it1 != s1.end())
		{
			*it1 -= 3;
			cout << *it1 << " ";
			++it1;
		}
		cout << endl;
		for (auto ch : s1)
		{
			cout << ch << " ";
		}
		cout << endl;
	}
	void test_string3()
	{
		string s1("hello world");
		string::const_iterator it1 = s1.begin();
		while (it1 != s1.end())
		{
			//*it1 -= 3;
			cout << *it1 << " ";
			++it1;
		}
		cout << endl;
		for (auto ch : s1)
		{
			cout << ch << " ";
		}
		cout << endl;
	}
	void test_string4()
	{
		string s1;
		s1.push_back('1');
		s1.push_back('2');
		s1.append("000");
		cout << s1.c_str() << endl;
		//for (auto ch : s1)
		//{
		//	cout << ch << " ";
		//}
		//cout << endl;
		s1 += 'x';
		cout << s1.c_str() << endl;
		s1 += "aaa";
		cout << s1.c_str() << endl;
 
	}
	void test_string5()
	{
		string s1("123456");
		cout << s1.size() << endl;
		//s1.insert(0, '0');
		//s1.insert(s1.size(), '7');
		//cout << s1.c_str() << endl;
		s1.insert(0, "xx");
		cout << s1.size() << endl;
		s1.insert(s1.size(), "888");
		cout << s1.size() << endl;
		cout << s1.capacity() << endl;
		cout << s1.c_str() << endl;
		s1.insert(1, "yy");
		cout << s1.c_str() << endl;
 
	}
	void test_string6()
	{
		string s1("0123456789");
		//s1.erase(0, 1);
		s1.erase(3, 3);
		s1.erase(3);
 
		cout << s1.c_str() << endl;
	}
	void test_string7()
	{
		string s1("hello world");
		string s2(s1);
		string s3;
		s3 = s1;
		s1.resize(20);
		cout << s1.c_str() << endl;
		s1.resize(5);
		cout << s1.c_str() << endl;
		s1.resize(20, 'x');
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
		cout << s3.c_str() << endl;
	}
	void test_string8()
	{
		string s1("hello world");
		string s2("xxxxx xxxxx");
		s1.swap(s2);
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
	}
	void test_string9()
	{
		string s1("hello world");
		cout << s1.find('l', 0) << endl;
		cout << s1.find('l', 5) << endl;
		//char ch[] = "wor";
		//cout << s1.find(ch) << endl;
		cout << s1.find("world",0) << endl;
		cout << s1.substr(5).c_str() << endl;
		cout << s1.substr(5, 2).c_str() << endl;
	}
	void test_string10()
	{
		string s1("123456");
		string s2("123456");
		cout << (s1 == s2) << endl;
		cout << ("123456" == s1) << endl;
		cout << (s1 == "123456") << endl;
	
		cout << s1 << endl;
		cin >> s1 >> s2;
		cout << s1;
		cout << s2;
	}
	void test_string11()
	{
		string s1;
		cout << s1 << endl;
		getline(cin, s1);
		cout << s1 << endl;
		cout << s1.capacity() << endl;
 
 
	}
}

这是几个测试函数

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
#include"Class_String_2.h"
//using namespace dc;
int main()
{
	//String s1();
	//String s1("hello word");
	//dc::test_string();
	//dc::test_string2();
	//dc::test_string3();
	//dc::test_string4();
	//dc::test_string5();
	//dc::test_string6();
	//dc::test_string7();
	//cout << typeid(std::string::iterator).name() << endl;
	//cout << typeid(dc::string::iterator).name() << endl;
	//dc::test_string8();
	//dc::test_string9();
	//dc::test_string10();
	dc::test_string11();
 
}

2 传统版写法的String类

class String
{
public:
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* pStr = new char[strlen(s._str) + 1];
			strcpy(pStr, s._str);
			delete[] _str;
			_str = pStr;
		}
		return *this;
	}
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

3 现代版写法的String类

class String
{
public:
	String(const char* str = "")
	{
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		: _str(nullptr)
	{
		String strTmp(s._str);
		swap(_str, strTmp._str);
	}
	// 对比下和上面的赋值那个实现比较好?
	String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
	/*
	String& operator=(const String& s)
	{
	if(this != &s)
	{
	String strTmp(s);
	swap(_str, strTmp._str);
	}return *this;
 }
 */
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

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

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

相关文章

STM32定时器详解(1)

文章目录 前言一、STM32中定时器的分类二、基础定时器2.1基础定时器框图讲解2.2基础定时器计数功能讲解 三、通用定时器3.1通用定时器基本描述3.2通用定时器硬件框图 四、高级定时器4.1高级定时器基本描述4.2高级定时器硬件框图 总结 前言 本篇文章将带大家来学习STM32中的定时…

SunFMEA冠翔(台山)工业FMEA培训会圆满结束

近日&#xff0c;SunFMEA软件成功在冠翔&#xff08;台山&#xff09;工业有限公司举办了为期三天的FMEA软件系统培训&#xff0c;通过重要知识讲解、现场答疑、演练互动、软件实操等环节&#xff0c;把培训氛围推向高潮。 ​ 此次培训分为DFMEA与PFMEA两部分&#xff0c;按照七…

浏览器工作原理与实践--TCP协议:如何保证页面文件能被完整送达浏览器

在衡量Web页面性能的时候有一个重要的指标叫“FP&#xff08;First Paint&#xff09;”&#xff0c;是指从页面加载到首次开始绘制的时长。这个指标直接影响了用户的跳出率&#xff0c;更快的页面响应意味着更多的PV、更高的参与度&#xff0c;以及更高的转化率。那什么影响FP…

不定方程求解【详解+代码】

不定方程求解 文章目录 不定方程求解题目描述 分析题目简单穷举法思路1&#xff1a;Java代码C代码 思路2Java代码C代码 测试结果 题目描述 给定正整数a&#xff0c;b&#xff0c;c。求不定方程 axbyc 关于未知数x和y的所有非负整数解组数。 Input 一行&#xff0c;包含三个正…

Linux离线部署gitLab及使用教程

一、下载gitLab的linux系统rpm包 地址&#xff1a;Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 找到这个最新版 点击下载 二、上传到linux系统 笔者是在windows系统下的vmware虚拟机中部署安装的&#xff0c;虚拟机中安装了cent…

c/c++整数和浮点数在内存中存储

了解变量的储存原理是我们灵活运用和防止数据截断改变带来的危害的有效途径。 那么我们从int char和float double两类来阐述内存的储存。 首先我们讲内存单位&#xff1a; 内存单位从小到大分别是bit byte KB MB GB TB PB。 bit是最小的内存单位&#xff0c;它可以存储一…

docker容器下部署hbase并在springboot中通过jdbc连接

我在windows的docker中部署了一个hbase服务&#xff0c;然后用springboot连接到此服务并访问数据。 详情可参考项目中的README.md。项目中提供了用于构建镜像的dockerfile&#xff0c;以及测试代码。 项目连接&#xff1a;https://gitee.com/forgot940629/hbase_phoenix_spring…

Java学习路线大纲

一、学习路线 二、学习大纲 0. 地基部分 数据结构&#xff1a;线性表、队列、栈、树、图、哈希等等常见算法&#xff1a;10大排序、字符串匹配、二分法、双指针等等操作系统&#xff1a;进行线程管理、内存管理、I/O等等计算机网络&#xff1a;四层协议、TCP/UDP、HTTP/HTTPS等…

安装elasticsearch和kibana

1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net 1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的镜像&#xff0c;这个镜像体积非常大&#xff0…

互联网思维:息共享、开放性、创新和快速反应、网络化、平台化、数据驱动和用户体验 人工智能思维:模拟人、解放劳动力、人工智能解决方案和服务

互联网思维&#xff1a;信息共享、开放性、创新和快速反应、网络化、平台化、数据驱动和用户体验 互联网思维是指一种以互联网为基础的思考方式&#xff0c;强调信息共享、开放性、创新和快速反应的特点。这种思维方式注重网络化、平台化、数据驱动和用户体验&#xff0c;以适…

数通-路由策略

路由策略 访问控制&#xff1a;1.acl控制——通过控制流量&#xff0c;起到控制作用。2.路由控制 注意&#xff1a;ACL在做报文过滤时&#xff0c;默认允许所有&#xff1b;在做路由抓取时&#xff0c;默认拒绝所有&#xff0c;且只能使用基本ACL。 路由控制 1、路由策略&a…

基于springboot的反诈宣传平台

技术&#xff1a;springbootmysqlvue 一、系统背景 反欺诈平台可以对公交信息进行集中管理&#xff0c;可以真正避免传统管理的缺陷。反欺诈平台是一款运用软件开发技术设计实现的应用系统&#xff0c;在信息处理上可以达到快速的目的&#xff0c;不管是针对数据添加&#xff…

电力柜智能蓝牙锁控解决方案

一、行业背景 随着智能电网的快速发展&#xff0c;电力柜作为电网的重要组成部分&#xff0c;其安全性和可靠性对于保障电力供应至关重要。传统的电力柜锁控系统多依赖于物理钥匙&#xff0c;存在管理不便、安全隐患大、难以实时监控等问题&#xff0c;为了提高电力柜的安全管…

AI绘画自动生成器:让艺术创作触手可及

随着人工智能技术的飞速发展&#xff0c;越来越多的应用领域逐渐与AI技术融合。在艺术领域&#xff0c;AI绘画自动生成器成为了一款备受关注的产品。它利用深度学习算法&#xff0c;让用户通过输入关键词或描述性文本&#xff0c;就能在几秒钟内生成一幅独特的艺术作品。在这篇…

探索人工智能基础:从概念到应用【文末送书-42】

文章目录 人工智能概念人工智能基础【文末送书-42】 人工智能概念 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;作为当今科技领域的热门话题&#xff0c;已经深刻地影响着我们的生活和工作。但是&#xff0c;要理解人工智能&#xff0c;我们首先需…

杂记8---多线激光雷达与相机外参标定

背景&#xff1a;本人开源的标定程序&#xff0c;提供大家参考学习 基于棋盘格的多线激光雷达和鱼眼/针孔模型相机外参标定的程序 前言 标定数据&#xff0c;只需要一个棋盘格标定板。把标定板放置lidar 与camera 共视区域&#xff0c;拜拍几个pose进行采集。 基于简谐原则…

快速傅氏变换(Fast Fourier Transform,FFT)算法基本原理详细解析

目录 目录 FFT 基本原理 FFT算法 Cooley-Tukey 步骤概述&#xff1a; 1、分解&#xff1a;将原始序列分成偶数部分和奇数部分。原始DFT问题就被分解成两个长度为N/2的子问题&#xff0c;分别对应偶数索引和奇数索引的元素。 2、递归&#xff1a;递归地对这两个子序列应用F…

多线程libtorch推理问题

一、环境 我出问题的测试环境如下: pytorch1.10+cu113 pytorch1.10+cu116 pytorch2.2+cu118 libtorch1.10.1+cu113 libtorch1.10.1+cu111 libtorch1.9.0+cu111 二、问题现象 最近封装libtorch的推理为多线程推理的时候,遇到一个现象如下: (1)只要是将模型初始化放到一个…

黑马现有java课程框架及其功能梳理

目录 高并发相关提高通信效率Netty作用&#xff1a;哪些框架使用它&#xff1a; ChannelChannelHandler 和 ChannelPipelineEventLoop 和 EventLoopGroup**涉及的名词解释&#xff1a;**NIOSocketNginx 高并发相关 主要用来解决IO密集型程序&#xff08;大量文件读写&#xff…

游戏软件报错xinput1_3.dll丢失如何修复,5种方法一分钟教你修复完成

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示或者程序无法正常运行的情况。其中&#xff0c;一个常见的问题就是与xinput13.dll文件相关的问题。那么&#xff0c;xinput13.dll到底是什么呢&#xff1f;本文将对其进行详细介绍&#xff0c;帮助大家更好地理解和解…