string的模拟全实现

news2024/11/16 21:46:32

请添加图片描述

文章目录

  • 📝前言
  • 🌠 string的基本要素
    • 🌉构造函数和析构函数
    • 🌠string()
      • 🌉string(const char* str = "");
    • 🌠~string()
      • 🌉深拷贝string(const string& s);
  • 🌠三个成员函数的实现
      • 🌉const size_t size() const;
      • 🌉const size_t capacity() const;
      • 🌉 const char* c_str() const;
  • 🌠迭代器
    • 🌉两个遍历下标实现
  • 🌠总代码
    • 🌉string.h
    • 🌉string.cpp
    • 🌉test.cpp
  • 🚩总结


📝前言

前面我们学习了string的用法,本节我们将实现string的模拟实现,话不多说,直接上手,因此我们先了解我们是多文件进行编写,因此需要注意命名空间的控制,这是文件分布图:OK,我们开始~
在这里插入图片描述
注:标注声明的是加在类的声明(string.h),定义在类的定义(string.cpp)如果漏写,那就是小标题就是声明🥰:

🌠 string的基本要素

我们看 成员变量三部分:

  • char* _str: 指向存储字符串内容的动态内存空间的指针。
  • size_t _size: 记录字符串的长度(不包括结尾的空字符'\0')。
  • size_t _capacity: 记录当前动态内存空间的容量。

因为模拟实现,我们避免跟库里的std::string冲突,我们需要定义在自己的命名空间,这样的好处除了避免冲突,在多个文件中,命名空间的内容是可以合并的,

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

namespace self
{
	class string
	{
	public:
	--------
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
}

在这里插入图片描述

🌉构造函数和析构函数

string有多种构造函数,默认构造函数,拷贝构造函数,我们可以实现string(),或者可以实现全缺省构造函数string(const char* str = "");我们只需要实现一个就可以了,防止调用错误:

🌠string()

声明:string();

空字符串构建:

string::string()
{
	_str = new char[1]{'\0'};
	_size = 0;
	_capacity = 0;
}

🌉string(const char* str = “”);

声明:string(const char* str = "");
定义:

第一种实现:

string(const char* str)
    : _str(new char[strlen(str)+1])
    , _size(strlen(str))
    , _capacity(strlen(str))
{
    strcpy(_str, str);
}

缺点:

  1. 在初始化列表中调用了strlen(str)两次,这可能会造成不必要的性能损耗。
  2. 在初始化列表中分配内存空间时,使用了strlen(str) + 1的大小,这可能会造成内存浪费。

优化后的代码:

string::string(const char* str)
    : _size(strlen(str))
{
    _capacity = _size;
    _str = new char[_size + 1];
    strcpy(_str, str);
}

优点:

  1. 在初始化列表中只调用了一次strlen(str),减少了不必要的性能损耗。
  2. 在构造函数体内分配内存空间时,使用了_size + 1的大小,更加精确地满足了字符串的需求,避免了内存浪费。

🌠~string()

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

🌉深拷贝string(const string& s);

在这里插入图片描述

String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构
造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块
空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

既然这样,开多一块空间存储分开存储s1,s2不就好了。
在这里插入图片描述

声明:string(const string& s);
定义实现:

string::string(const string& s)
{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
}
  1. 在初始化列表中,使用new char[s._capacity + 1]分配了一块新的内存空间,大小为参数s的容量加1(用于存储字符串结尾的空字符)。
  2. 使用strcpy()函数将参数s的字符串内容复制到新分配的内存空间中。
  3. 将新创建的字符串长度(s._size)和容量(s._capacity)赋值给当前对象的成员变量_size_capacity

🌠三个成员函数的实现

🌉const size_t size() const;

返回当前string对象中存储的字符串的长度。

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

🌉const size_t capacity() const;

返回当前string对象的容量,即可以存储的最大字符数。

const size_t string::capacity() const
	{
		return _capacity;
	}

🌉 const char* c_str() const;

返回一个指向string对象内部存储的 C 风格字符串的指针。

const char* string::c_str() const
	{
		return _str;
	}
const size_t string::size() const
	{
		return _size;
	}

🌠迭代器

迭代器是一种抽象的概念,用于遍历容器中的元素。在string类中,迭代器用于遍历字符串中的字符。

typedef char* iterator;
typedef const char* const_iterator;

iterator begin();
iterator end();

iterator begin() const;
iterator end() const;
  1. 类型定义:
    • typedef char* iterator;: 定义了一个名为iterator的类型,它是一个指向char类型的指针。这个迭代器可以用于修改字符串中的字符。
    • typedef const char* const_iterator;: 定义了一个名为const_iterator的类型,它是一个指向const char类型的指针。这个迭代器只能用于读取字符串中的字符,不能修改它们。
  2. 成员函数:
    • iterator begin();: 返回一个指向字符串首字符的迭代器。
    • iterator end();: 返回一个指向字符串末尾字符的下一个位置的迭代器。
    • iterator begin() const;: 返回一个指向字符串首字符的只读迭代器。
    • iterator end() const;: 返回一个指向字符串末尾字符的下一个位置的只读迭代器。
      定义实现:
string::iterator string::begin()
{
	return _str;
}

string::iterator string::end()
{
	return _str + _size;
}

string::iterator string::begin() const
{
	return _str;
}

string::iterator string::end() const
{
	return _str + _size;
}

这些迭代器可以用于遍历string对象中的字符,例如使用for循环或标准库算法。非常量版本的迭代器可以用于修改字符串中的字符,而常量版本的迭代器只能用于读取字符串中的字符。这种设计提供了灵活性,使用户可以选择合适的迭代器来满足不同的需求。

🌉两个遍历下标实现

下标运算符[]允许用户通过下标访问字符串中的字符。

声明:

char& operator[](size_t pos);
const char& operator[](size_t pos) const;

成员函数声明:

  • char& operator[](size_t pos);: 重载了下标运算符[],接受一个size_t类型的下标作为参数,返回一个对字符串中对应位置的字符的引用。这个版本的下标运算符可以用于修改字符串中的字符。
  • const char& operator[](size_t pos) const;: 重载了下标运算符[],接受一个size_t类型的下标作为参数,返回一个对字符串中对应位置的字符的常量引用。这个版本的下标运算符只能用于读取字符串中的字符,不能修改它们。
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];
}

成员函数实现:

  • char& string::operator[](size_t pos): 首先使用assert断言检查下标是否在字符串的范围内,然后返回_str[pos]的引用。这允许用户通过下标修改字符串中的字符。
  • const char& string::operator[](size_t pos) const: 同样使用assert断言检查下标是否在字符串的范围内,然后返回_str[pos]的常量引用。这只允许用户通过下标读取字符串中的字符,不能修改它们。

🌠总代码

string的接口函数远远不止我介绍的这几种,阿森也会加更其他接口函数,以下是全代码的的接口和测试:

🌉string.h

// 禁用安全警告,这通常用于使用某些可能被认为是不安全的函数时
#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream> // 引入输入输出流库
#include <assert.h> // 引入断言库
using namespace std; // 使用标准命名空间

// 自定义命名空间self
namespace self {

    // 自定义字符串类
    class string {
    public:
        // 定义迭代器类型
        typedef char* iterator;
        typedef const char* const_iterator;

        // 构造函数
        string(const char* str = ""); // 使用C风格字符串初始化
        string(const string& s); // 拷贝构造函数

        // 赋值运算符重载
        string& operator=(const string& s);

        // 析构函数
        ~string();

        // 获取字符串大小
        const size_t size() const;

        // 获取C风格字符串
        const char* c_str() const;

        // 获取容量
        const size_t capacity() const;

        // 索引运算符重载
        char& operator[](size_t pos);
        const char& operator[](size_t pos) const;

        // 获取迭代器指向字符串开始和结束位置
        iterator begin();
        iterator end();

        // 获取常量迭代器指向字符串开始和结束位置
        iterator begin() const;
        iterator end() const;

        // 预留空间
        void reserve(size_t n);

        // 在字符串末尾添加一个字符
        void push_back(char ch);

        // 追加一个C风格字符串
        void append(const char* tmp);

        // 加法赋值运算符重载
        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 = 0, size_t len = npos);

        // 查找字符或字符串
        size_t find(char c, size_t pos = 0);
        size_t find(const char* s, size_t pos = 0);

        // 交换两个字符串
        void swap(string& str);

        // 获取子字符串
        string substr(size_t pos = 0, size_t len = npos);

        // 比较运算符重载
        bool operator<(const string& s) const;
        bool operator>(const string& s) const;
        bool operator<=(const string& s) const;
        bool operator>=(const string& s) const;
        bool operator==(const string& s) const;
        bool operator!=(const string& s) const;

        // 清空字符串
        void clear();

    private:
        // 字符串数据
        char* _str;
        // 字符串当前大小
        size_t _size;
        // 字符串容量
        size_t _capacity;

        // 静态常量npos,表示位置无效
        const static size_t npos;
        		//特例
		//const static size_t npos = -1;
		
		//只有size_t能支持,其他类型都不可以
		//const static double npos = -1;

    };

    // 输入运算符重载
    istream& operator>>(istream& is, string& str);

    // 输出运算符重载
    ostream& operator<<(ostream& os, const string& str);
}

// 注意:这里只是声明了npos常量,并未初始化。它应该在类的实现文件(.cpp)中进行初始化,
// 初始化为一个表示无效位置的常量值,通常是size_t的最大值或-1。

🌉string.cpp

# define _CRT_SECURE_NO_WARNINGS 1
#include "string.h"
namespace self
{
	const  size_t string::npos = -1;


	//string::string()
	//{
	//	_str = new char[1]{'\0'};
	//	_size = 0;
	//	_capacity = 0;
	//}
	string::string(const char* str)
		:_size(strlen(str))
	{
		_capacity = _size;
		_str = new char[_size + 1];
		strcpy(_str, str);
	}

	//s2(s1)
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}

	// s1 = s3
	// s1 = s1
	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			char* tmp = new char[_capacity + 1];
			strcpy(_str, s._str);
			delete[] _str;
			_size = s._size;
			_capacity = s._capacity;
		}

		return *this;
	}

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

	const char* string::c_str() const
	{
		return _str;
	}

	const size_t string::capacity() const
	{
		return _capacity;
	}

	string::iterator string::begin()
	{
		return _str;
	}

	string::iterator string::end()
	{
		return _str + _size;
	}

	string::iterator string::begin() const
	{
		return _str;
	}

	string::iterator string::end() const
	{
		return _str + _size;
	}

	const 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];
	}


	 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 : 2 * _capacity;
			 reserve(newcapacity);
		 }

		 _str[_size] = ch;
		 _str[_size + 1] = '\0';
		 ++_size;
	 }

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

		 //for (size_t i = 0; i <= len; i++)
		 //{
			// _str[_size + i] = tmp[i];
		 //}

		 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, char ch)
	 {
		 assert(pos < _size);
		 if (_size == _capacity)
		 {
			 size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			 reserve(newcapacity);
		 }
		 size_t end = _size + 1;
		 while (end > pos)
		 {
			 _str[end] = _str[end - 1];
			 end--;
		 }

		 _str[pos] = ch;
		 ++_size;
	 }

	 void string::insert(size_t pos, const char* str)
	 {
		 assert(pos < _size);
		 size_t len = strlen(str);
		 if (_size + len > _capacity)
		 {
			 reserve(_size + len);
		 }
		 size_t lenth = strlen(str);
		 size_t end = _size + lenth;
		 while (end > pos + lenth - 1)
		 {
			 _str[end] = _str[end - lenth];
			 --end;
		 }

		 memmove(_str + pos, str, lenth);
		 _size += lenth;
	 }

	 void string::erase(size_t pos , size_t len )
	 {
		 assert(pos < _size);
		 //要删除的长度比pos位置长,有多少删多少
		 if (len > _size - pos)
		 {
			 _str[pos] = '\0';
			 _size = len;
		 }
		 else
		 {
			 strcpy(_str + pos, _str + pos + len);
			 _size -= len;
		 }
	 }

	 //找到返回下标
	 size_t string::find(char c, size_t pos)
	 {
		 assert(pos < _size);
		 for (size_t i = pos; i < _size; i++)
		 {
			 if (_str[i] == c)
			 {
				 return i;
			 }
		 }

		 return npos;

	 }

	 size_t string::find(const char* str, size_t pos)
	 {
		 assert(pos < _size);
		 const char* pch = strstr(_str + pos , str);
		 if (pch == nullptr)
		 {
			 return npos;
		 }
		 else
		 {
			 return pch - _str;
		 }
	 }

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


	 string string::substr(size_t pos, size_t len)
	 {
		 len = (len == npos) ? (_size - pos) : len;// 如果 len 为 npos,则设置为字符串剩余长度
		 
		 if (pos >= _size)// 如果 pos 超出字符串长度,则返回空字符串
			 return string::string();
		 
		 len = (pos + len >= _size) ? (_size - pos) : len;// 如果子字符串超出原始字符串的末尾,则调整 len
		 
		 char* new_str = new char[len + 1];
		 strncpy(new_str, _str + pos, len);
		 new_str[len] = '\0';

		 return string(new_str); //返回子字符串的 bit::string 对象
	}


		//这个sub为局部对象,返回会析构,
	 //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;
		// }
	 //}


	 bool string::operator<(const string& s) const
	 {
		 return strcmp(_str, s._str) < 0;
	 }

	 bool string::operator>(const string& s) const
	 {
		 return strcmp(_str, s._str) > 0;
	 }

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

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

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

	 bool string::operator!=(const string& s) const
	 {
		 return !(*this == s);
	 }
	
	 void string::clear()
	 {
		 _str[0] = '\0';
		 _size = 0;
	 }

	 //istream& operator>>(istream& is, string& str)
	 //{
		// str.clear();
		// char ch ='a';
		// scanf("%c", &ch);
		// while (ch != ' ' && ch != '\n')
		// {
		//	 str += ch;
		//	 scanf("%c", &ch);

		// }

		// return is;
	 //}

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

		 }

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

		 return os;
	 }

}

🌉test.cpp

# define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include "string.h"
namespace self
{
	void test_string01()
	{
		self::string s1("hello world");
		cout << s1.c_str() << endl;

		string::iterator it = s1.begin();
		while (it != s1.end())
		{
	        //it = 'y';
			cout << *it << " ";
			it++;
		}
		cout << endl;

		string::const_iterator it2 = s1.begin();
		while(it2 != s1.end())
		{ 
			//*it2 = 'y';
			cout << *it2;
			it2++;
		}
		cout << endl;

		for (auto e : s1)
		{
			cout << e << endl;
			e++;
		}
		cout << endl;

		const self::string s2("xxxxxxx");
		for (size_t i = 0; i < s2.size(); i++)
		{
			//s2[i] = 'y';
		}
		for (size_t i = 0; i < s2.size(); i++)
		{
			cout << s2[i] << ' ';
		}
		for (auto& e : s2)
		{
			cout << e << endl;
			e++;
		}
		cout << endl;
	}

	void test_string02()
	{
		self::string s3("hello world");
		cout << s3.c_str() << endl;
		
		s3.push_back('x');
		cout << s3.c_str() << endl;

		s3.push_back('y');
		cout << s3.c_str() << endl;

		s3.push_back('z');
		cout << s3.c_str() << endl;

		self::string s4("hello world");
		cout << s4.c_str() << endl;
		
		s4.append("xxxxxx");
		cout << s4.c_str() << endl;

		s4.append("yyyyyy");
		cout << s4.c_str() << endl;
	}

	void test_string03()
	{
		self::string s5("hello world");
		cout << s5.c_str() << endl;
		
		s5 += 'x';
		cout << s5.c_str() << endl;
	
		s5 += "yyyyyy";
		cout << s5.c_str() << endl;

	}

	void test_string04()
	{
		self::string s6("hello world");
		cout << s6.c_str() << endl;

		s6.insert(2, 'x');
		cout << s6.c_str() << endl;

		s6.insert(3, "yyy");
		cout << s6.c_str() << endl;
		
		s6.erase(2, 1);
		cout << s6.c_str() << endl;
		
		s6.erase(2, 3);
		cout << s6.c_str() << endl;
	}

	void test_string05()
	{
		self::string s7("hello world");
		cout << s7.c_str() << endl;

		size_t location = s7.find('h', 1);
		cout << location<< endl;

		size_t location02 = s7.find("he", 1);
		cout << location02 << endl;
	}

	void test_string06()
	{
		self::string buyer("money");
		self::string seller("goods");

		cout << "Before the swap, buyer has " << buyer.c_str();
		cout << " and seller has " << seller.c_str() << '\n';

		buyer.swap(seller);

		cout << " After the swap, buyer has " << buyer.c_str();
		cout << " and seller has " << seller.c_str() << '\n';
	}

	void test_string07()
	{
		self::string str = "We think in generalities, but we live in details.";

		string str2 = str.substr(3, 5);     // "think"

		cout << str2.c_str() << ' ' ;

	}

	void test_string08()
	{
		self::string url("https://legacy.cplusplus.com/reference/string/string/");
		size_t pos1 = url.find(':', 1);
		self::string url1 = url.substr(0, pos1 - 0);
		cout << url1.c_str() << endl << endl;

		cout << pos1 << endl;
		size_t pos2 = url.find('/', pos1 + 3);
		cout << pos2 << endl;

		self::string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));
		cout << url2.c_str() << endl << endl;

		self::string url3 = url.substr(pos2 + 1);
		cout << url3.c_str() << endl;
	}

	void test_string09() 
	{
		self::string s1("hello world");
		cout << s1 << endl;

		cin >> s1;
		cout << s1 << endl;
	}

	void test_string10()
	{
		self::string s8("hello world");
		cout << s8.c_str() << endl;

		string s9(s8);
		cout << s9.c_str() << endl;

		string s10 = s8;
		cout << s10.c_str() << endl;


	}
}
int main()
{
	//self::test_string01();
	//self::test_string02();
	//self::test_string03();
	//self::test_string04();
	//self::test_string05();
	//self::test_string06();
	//self::test_string07();
	//self::test_string08();
	//self::test_string09();
	//self::test_string10();

	string s1("hello world");
	return 0;
}

测试一个部分:
在这里插入图片描述


🚩总结

请添加图片描述

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

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

相关文章

外网ip地址怎么获取?快解析

大家都清楚互联网是通过ip地址通信的&#xff0c;ip地址又分内网ip和外网ip。内网ip只能在内网使用&#xff1b;而外网ip作为电脑唯一标识&#xff0c;可在公网使用。那么外网ip地址怎么获取呢&#xff1f; 外网ip是网络运营商分配给用户的。目前最常见的两种上网方式一个是拉…

金航标kinghelm萨科微slkor公司发展和品牌传播

为了金航标kinghelm萨科微slkor公司发展和品牌传播&#xff0c;推出了”金航标每日芯闻“与“萨科微每日芯闻“栏目&#xff0c;影响力非常好。这一构想并非一时的灵光乍现&#xff0c;而是经过深思熟虑和充分调研的结果。制定该栏目的模板时候时&#xff0c;就明确了要求语音版…

【nfs服务部署服务端和客户端搭建】

原理 NFS&#xff08;Network File System&#xff09;是文件服务器之一。它的功能是可以通过网络&#xff0c;让不同的机器、不同的操作系统可以彼此共享数据文件。 NFS服务器可以让服务端的共享目录挂载到本地端的文件系统中&#xff0c;其他服务器如果想访问共享目录&#…

windows 安装 Conda

1 Conda简介 Conda 是一个开源的软件包管理系统和环境管理系统&#xff0c;用于安装多个版本的软件包及其依赖关系&#xff0c;并在它们之间轻松切换。Conda 是为 Python 程序创建的&#xff0c;适用于 Linux&#xff0c;OS X 和Windows&#xff0c;也可以打包和分发其他软…

键盘和鼠标的隐形观察者:用Python的pynput库记录每一个动作

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 揭秘pynput&#xff1a;监控神器还是隐私威胁&#xff1f; 在数字时代&#xff0c;的每一次键盘敲击和鼠标点击都可能泄露信息。但如果能够控制这一过程&#xff0c;又将如何利用这些数据呢&#xff1f;Python的pyn…

“Linux”目录结构and配置网络

了解完命令格式和vi、vim编辑器后&#xff0c;我们来认识一下目录的结构&#xff1a; 一、目录 &#xff08;1&#xff09;目录的特点 windows特点&#xff1a; Windows中有C、D、E盘&#xff0c;每个都是一个根系统 Linux特点&#xff1a; linux中只有一个根&#xff08;单…

【opencv】opencv透视变换和ocr识别实验

实验环境&#xff1a;anaconda、jupyter notebook 实验用到的包opencv、numpy、matplotlib、tesseract 一、opencv透视变换 原图 图片是我拍的耳机说明书&#xff0c;哈哈哈哈&#xff0c;你也可以使用自己拍的照片&#xff0c;最好是英文内容&#xff0c;tesseract默认识别英…

C++|多态性与虚函数(2)|虚析构函数|重载函数|纯虚函数|抽象类

前言 看这篇之前&#xff0c;可以先看多态性与虚函数&#xff08;1&#xff09;⬇️ C|多态性与虚函数&#xff08;1&#xff09;功能绑定|向上转换类型|虚函数-CSDN博客https://blog.csdn.net/weixin_74197067/article/details/138861418?spm1001.2014.3001.5501这篇文章会…

Kasawaki川崎机器人故障维修

在当今的自动化工业领域&#xff0c;川崎工业机器人以其卓越的性能和可靠的工作效率赢得了广泛的赞誉。作为机器人的核心组成部分&#xff0c;伺服电机的作用至关重要。然而&#xff0c;就像所有机械设备一样&#xff0c;也可能会遭遇电机磨损或故障&#xff0c;需要适时的川崎…

二叉树——初解

二叉树 树树的概念树的性质 二叉树二叉树的概念二叉树的性质二叉树的实现方式数组构建左孩子右兄弟法构建指针构建 树 树的概念 在计算机科学中&#xff0c;树&#xff08;Tree&#xff09;是一种重要的非线性数据结构&#xff0c;它由若干节点&#xff08;Node&#xff09;组…

iPhone15销量不佳,新产品没希望,苹果开始寻找库克接班人

美国媒体开始谈论谁将成为苹果的新CEO&#xff0c;这意味着苹果董事会开始为库克寻找接班人了&#xff0c;导致如此结果&#xff0c;可能在于iPhone15的表现实在太差了&#xff0c;而库克力推的vision Pro等新产品又没有为苹果打开局面所致。 如今的苹果倒是与1980年代乔布斯离…

壹资源知识付费系统源码-小程序端+pc端

最新整理优化&#xff0c;含微信小程序和pc网页。内置几款主题&#xff0c;并且可以自己更改主题样式&#xff0c;各区块颜色&#xff0c;文字按钮等。 适用于知识付费类资源类行业。如&#xff1a;项目类&#xff0c;小吃技术类&#xff0c;图书类&#xff0c;考研资料类&…

Nodejs 第七十一章(libuv)

libuv 在Node.js中&#xff0c;libuv是作为其事件循环和异步I/O的核心组件而存在的。Node.js是构建在libuv之上的&#xff0c;它利用libuv来处理底层的异步操作&#xff0c;如文件I/O、网络通信和定时器等。 libuv在Node.js中扮演了以下几个重要角色&#xff1a; 事件循环&a…

同为科技详解智能PDU所应用的通信协议与接口

现如今&#xff0c;信息服务、AI人工智能的飞速发展与增长&#xff0c;全球正经历信息数据的爆炸。不仅数据量以惊人的速度增长&#xff0c;而且全球社会各行业对数据的依赖的程度也在日益增加。这些趋势使数据中心在全球都享有关键基础架构的地位。假设某个数据中心发生严重的…

写一个类ChatGPT应用,前后端数据交互有哪几种

❝ 对世界的态度&#xff0c;本质都是对自己的态度 ❞ 大家好&#xff0c;我是「柒八九」。一个「专注于前端开发技术/Rust及AI应用知识分享」的Coder 前言 最近&#xff0c;公司有一个AI项目&#xff0c;要做一个文档问答的AI产品。前端部分呢&#xff0c;还是「友好借鉴」Cha…

经典神经网络(8)GAN、CGAN、DCGAN、LSGAN及其在MNIST数据集上的应用

经典神经网络(8)GAN、CGAN、DCGAN、LSGAN及其在MNIST数据集上的应用 1 GAN的简述及其在MNIST数据集上的应用 GAN模型主导了生成式建模的前一个时代&#xff0c;但由于训练过程中的不稳定性&#xff0c;对GAN进行扩展需要仔细调整网络结构和训练考虑&#xff0c;因此GANs虽然在…

【js逆向】易车网JS逆向案例实战手把手教学(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

一表捋清网络安全等级保护测评要求

三级网络安全等级保护测评指标&#xff1a; 对于中小企事业单位来说&#xff0c;网络安全建设是一个复杂且投入较高的过程&#xff0c;因此他们更倾向于寻找一种“省心省力”的等保建设方案&#xff0c;以及一种能够持续有效且具有较高性价比的网络安全建设投入方式。 此时&…

合合信息:TextIn文档解析技术与高精度文本向量化模型再加速

文章目录 前言现有大模型文档解析问题表格无法解析无法按照阅读顺序解析文档编码错误 诉求文档解析技术技术难点技术架构关键技术回根溯源 文本向量化模型结语 前言 随着人工智能技术的持续演进&#xff0c;大语言模型在我们日常生活中正逐渐占据举足轻重的地位。大模型语言通…