【C++学习手札】模拟实现string

                                                     🎬慕斯主页修仙—别有洞天

                                                       ♈️今日夜电波:缶ビール—みゆな

                                                                0:41━━━━━━️💟──────── 2:52
                                                                    🔄   ◀️   ⏸   ▶️    ☰ 

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

一、string实际的底层原理

二、string的模拟实现

        基本成员函数 

        构造函数 

        拷贝构造函数

        析构函数

        重载赋值运算符 

        迭代器

        迭代器的概念 

        begin()

        end()

        空间管理

        修改相关

        push_back()

        append ()​编辑

        重载+=运算符

        c_str ()

        clear()

        swap()

         重载[ ](最爱的运算符!!!)

         关系运算符的重载

         基本操作

        查找find() 

        前置知识substr()

        插入insert()

        删除 erase()

        流插入流提取

 总体代码


一、string实际的底层原理

        string类型底层实现通常使用动态数组,因为它允许在运行时动态分配内存空间。这使得字符串的长度可以根据需要进行调整,而不需要提前分配固定大小的空间,并且可以避免缓冲区溢出和内存泄漏等问题。 string类型还提供了各种字符串操作函数,例如拼接、查找、替换、大小写转换等。

    底层实际的储存结构:

class string
{

private:

    char* _str;

    size_t _capacity;

    size_t _size;

};

二、string的模拟实现

        基本成员函数 

        构造函数 

        通过设置缺省参数以及初始化列表来默认构造string对象

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

        在模拟实现string的构造函数时,很多人包括作者我一开始都会想着将他分为有参以及无参两部分,但是这种做法是会产生一定的问题的,如下为作者写出的错误的代码:

class string
    {

    public:
        string()//无参构造函数
            //初始化列表
            :_str(nullptr)
            , _size(0)
            , _capacity(0)
        {

        }
        string(char* str)//带参构造函数->char a[] = "abc";string kk(a)
            :_str(str),
            _size(strlen(str)),
            _capacity(strlen(str))
        {

        }

        string(const char* str)//带参构造函数->string kk("abc")
            :_str(str),
            _size(strlen(str)),
            _capacity(strlen(str))
        {

        }
    private:

        char* _str;

        size_t _capacity;

        size_t _size;

    };

        对于以上代码,若调用如上的带参构造函数中string(const char* str)就会报错,因为str传给_str属于权限放大,现在有两种解决方法,一种是将它改为 string(char* str),但是这样就不能直接传递字符串,需要先生成字符串,还有一种方法是将_str改为const char*类型,但是此时就不能修改_str指向的内容,后续的一些操作会出现一些错误

         拷贝构造函数

        由于我们使用了开辟了空间来用于储存数据,因此我们需要进行深拷贝,防止对于另外一个对象的影响。

        传统写法: 

        开辟对应的空间,然后再给相应的值。

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

        较为推荐的写法:

        通过让构造函数帮我们创建一个构造相应的字符的对象,然后我们再交换两个对象的数据即可得。

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

        string(const string& s)
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            string tmp(s._str);
            swap(tmp);
        }

        析构函数

        避免内存泄漏需要自己写一个析构函数用以销毁new在堆区所创建的空间 

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

        重载赋值运算符 

         同上面的拷贝构造函数一样,我们对此也是需要深拷贝的

        传统写法: 

        开辟对应的空间,然后再给相应的值。

        //string& operator=(const string& s)
        //{
        //	if (this != &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& 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;
        }

         迭代器

        迭代器的概念 

         C++迭代器是一种用于遍历容器中元素的对象,它提供了一种统一的接口,使得不同类型的容器都可以使用相同的方式进行遍历。迭代器可以指向容器中的某个元素,并用于访问该元素的值或者进行修改。迭代器通常是一个类,它重载了指针操作符(*和->),以及自增(++)和自减(--)运算符,使得迭代器可以像指针一样进行移动。

        string的迭代器实际实现原理:

        string的迭代器实际上是一个指向string底层存储空间中char类型数据的指针,不同的迭代器类型标志着不同的访问权限。例如,一个string::iterator对象可以用来读写字符,而一个const string::iterator对象只能用来读取字符。

        于是我们定义出以下的迭代器:

        typedef char* iterator;
        typedef const char* const_iterator;

         begin()

        用于返回第一个字符的地址。

        iterator begin()
        {
            return _str; 
        }
        const_iterator begin()const
        {
            return _str; 
        }

      end()

        用于返回后一个字符的后一个字符的地址的地址。其实就是‘\0’的地址

        iterator begin()
        {
            return _str; 
        }
        const_iterator begin()const
        {
            return _str; 
        }

        空间管理

        size_t size()const//大小
        {
            return _size;
        }

        size_t capacity()const//空间
        {
            return _capacity;
        }

        bool empty()const//空?
        {
            return _size == 0;
        }

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

                _str = tmp;
                _capacity = n;
            }
        }

        void resize(size_t n, char c = '\0')//重设大小
        {
            if (n <= _size)
            {
                _str[n] = '\0';
                _size = n;
            }
            else 
            {
                reserve(n);
                while (_size < n)
                { 
                    _str[_size] = c ;
                    ++_size;
                }

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

         修改相关

         push_back()

         根据原型可知此函数是在当前字符串尾插一个字符,其中需要额外注意容量。

        void push_back(char c)
        {
            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }

            _str[_size] = c;
            ++_size;
            _str[_size] = '\0';
        }

        append ()

         根据原型可知此函数有很多的函数重载,但是我们最常用的就是尾插一段字符串,对此就不过多进行阐述。也需要额外注意容量。

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

            strcpy(_str + _size, str);//拷贝到原字符串后
            _size += len;
        }

         重载+=运算符

         此处复用了以上的push_back和append函数,这也是根据C++中函数管用的复用习惯来做出的选择。

 string& operator+=(char c)
        {
            push_back(c);
        }

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

            return *this;
        }

        c_str ()

        返回指向数组的指针,该数组包含以 null 结尾的字符序列(即 C 字符串),该字符表示字符串对象的当前值。简而言之就是将string对象转换为C语言风格类型的字符串。

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

        clear()

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

        swap()

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

         重载[ ](最爱的运算符!!!)

        这里其实就是按照数组的相关操作即可,但是要注意越界的判断!

        char& operator[](size_t index)
        {
            assert(index < _size);

            return _str[index];
        }

        const char& operator[](size_t index)const
        {
            assert(index < _size);

            return _str[index];
        }

         关系运算符的重载

        关系运算符的重载的关键就是根据所需进行相应的数据变化以及做出值的返回。 

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

        bool operator==(const string& s) const
        {
            return strcmp(_str, s._str) == 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);
        }

         基本操作

        查找find() 

        由原型知,分为按照字符查找以及字符串查找还有常量字符串查找,对此,此次实现字符以及字符串查找。

        对于字符查找: 

        size_t find(char c, size_t pos = 0) const
        {
            for (size_t i = pos; i < _size; i++)
            {
                if (_str[i] == c)
                {
                    return i;
                }
            }

            return npos;
        }

        对于字符串查找: 

        前置知识substr()

         他的主要作用就是在str中从pos位置开始,截取n个字符,然后将其返回。其中的npos实际上是一个全局的静态变量,实际的赋值为-1,都是由于类型为size_t,因此实际上为最大值!

        string substr(size_t pos, size_t len = npos)
        {
            string s;
            size_t end = pos + len;
            if (len == npos || pos + len >= _size) // 有多少取多少
            {
                len = _size - pos;
                end = _size;
            }

            s.reserve(len);
            for (size_t i = pos; i < end; i++)
            {
                s += _str[i];
            }

            return s;
        }

        // 返回子串s在string中第一次出现的位置
        size_t find(const char* s, size_t pos = 0) const
        {
            const char* p = strstr(_str + pos, s);
            if (p)
            {
                return p - _str;
            }
            else
            {
                return npos;
            }
        }

         插入insert()

        根据所插入的空间大小挪字符



        void insert(size_t pos, char ch)
        {
            assert(pos <= _size);
            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }

            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);
            }

            // 挪动数据
            int end = _size;
            while (end >= (int)pos)
            {
                _str[end + len] = _str[end];
                --end;
            }

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

        删除 erase()

         删除指定位置的字符,可根据调节删除的数量,默认是指定位置以后全删。

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

            if (len == npos || pos + len >= _size)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                size_t begin = pos + len;
                while (begin <= _size)
                {
                    _str[begin - len] = _str[begin];
                    ++begin;
                }
                _size -= len;
            }
        }

        流插入流提取

         重载<<和>>是为了能够像内置一样调用,输入打印。

ostream& operator<<(ostream& out, const 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, string& s)
{

    s.clear();

    char buff[129];
    size_t i = 0;

    char ch;
    ch = in.get();
    while (ch != ' ' && ch != '\n')
    {
        buff[i++] = ch;
        if (i == 128)
        {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }

        //s += ch;

        ch = in.get();
    }

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

    return in;
}

 总体代码

#pragma once
#define _CRT_SECURE_NO_WARNINGS 01
#include<iostream>
#include<assert.h>
using namespace std;


namespace lt
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		friend ostream& operator<<(ostream& out, const string& s);
		friend istream& operator>>(istream& in, string& s);
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

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

		/*string()
			:_str(new char[1]{'\0'})
			,_size(0)
			,_capacity(0)
		{}*/


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

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


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

		//		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);
		}

		// s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

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

		//	return *this;
		//}

		// s2 = s3
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

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

		char& operator[](size_t pos)
		{
			assert(pos < _size);

			return _str[pos];
		}

		const char& operator[](size_t pos) const
		{
			assert(pos < _size);

			return _str[pos];
		}

		size_t capacity() const
		{
			return _capacity;
		}

		size_t size() const
		{
			return _size;
		}

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

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

				_capacity = n;
			}
		}

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

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

		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 char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || pos + len >= _size) // 有多少取多少
			{
				len = _size - pos;
				end = _size;
			}

			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

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

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

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

		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);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			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);
			}

			// 挪动数据
			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			strncpy(_str + pos, str, len);
			_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;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}
				_size -= len;
			}
		}

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

		bool operator==(const string& s) const
		{
			return strcmp(_str, s._str) == 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);
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		//const static size_t npos = -1;  // 特例
		//const static double npos = 1.1;  // 不支持
	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const 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, string& s)
	{
		s.clear();
		//s.reserve(128);

		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//s += ch;

			ch = in.get();
		}

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

		return in;
	}


}

                      感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                         给个三连再走嘛~  

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

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

相关文章

2023年中国逆流式冷却塔性能特点、应用领域及市场规模分析[图]

按冷却塔热交换时气流和水流方向不同的配置&#xff0c;机力通风冷却塔又可分为横流式冷却塔、逆流式冷却塔&#xff0c;目前主流的冷却塔型式为逆流式冷却塔&#xff0c;逆流式冷却塔&#xff08;counterflowcoolingtower&#xff09;是指水流在塔内垂直落下&#xff0c;气流方…

员工电脑管理软件,企业电脑管理软件是什么

员工电脑管理软件&#xff0c;企业电脑管理软件是什么 企业电脑管理软件是指用于管理和监控企业员工工作电脑的软件。这些软件通常提供多种功能&#xff0c;旨在帮助企业管理员工电脑的使用、监控和维护&#xff0c;同时确保信息安全、提高生产力并确保合规性。推荐一款功能强…

java计算两个字符串日期相隔天数

java计算两个字符串日期相隔天数 public static void main(String[] args) throws ParseException {Scanner sc new Scanner(System.in);System.out.print("请输入计算开始的日期(yyyy-MM-dd):");String startTime sc.next();System.out.print("请输入计算结…

算法-链表-简单-相交、反转、回文、环形、合并

记录一下算法题的学习5 在写关于链表的题目之前&#xff0c;我们应该熟悉回忆一下链表的具体内容 什么是链表&#xff1a; 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按线性的顺序存储数据&#xff0c…

Windows电脑画面如何投屏到电视?怎样限定投屏内容?

电视通常比计算机屏幕更大&#xff0c;因此将电脑画面投射到电视上可以提供更广阔的视野和更好的视觉体验。通过将电脑画面投射到电视上&#xff0c;您可以与他人共享您的计算机屏幕上的内容。这对于展示演示文稿、观看影片或与他人分享照片等活动非常有用。 如果你的电脑系统是…

Azure的AI使用-(语言检测、图像分析、图像文本识别)

1.语言检测 安装包&#xff1a; # 语言检测 %pip install azure-ai-textanalytics5.2.0 需要用到密钥和资源的终结点&#xff0c;所以去Azure上创建资源&#xff0c;我这个是创建好的了然后点击密钥和终结者去拿到key和终结点 两个密钥选择哪个都行 语言检测代码示例&#…

Vue数据绑定

在我们Vue当中有两种数据绑定的方法 1.单向绑定 2.双向绑定 让我为大家介绍一下吧&#xff01; 1、单向绑定(v-bind) 数据只能从data流向页面 举个例子&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"…

图论| 827. 最大人工岛 127. 单词接龙

827. 最大人工岛 题目&#xff1a;给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。返回执行此操作后&#xff0c;grid 中最大的岛屿面积是多少&#xff1f; 岛屿 由一组上、下、左、右四个方向相连的 1 形成。 题目链接&#xff1a;[827. 最大人工岛](ht…

佳易王麻将馆计时收费系统怎么安装,麻将馆的灯控什么原理?

佳易王麻将馆计时收费系统怎么安装&#xff0c;麻将馆的灯控什么原理&#xff1f; 佳易王计时计费软件只需将压缩包文件解压即可使用&#xff0c;灯控的原理是&#xff1a;软件在点击开始计时的时候&#xff0c;软件向灯控器发送开灯信号&#xff0c;相应的灯打开&#xff0c;…

【面试】测试/测开(未完成)

1. 黑盒测试方法 黑盒测试&#xff1a;关注的是软件功能的实现&#xff0c;关注功能实现是否满足需求&#xff0c;测试对象是基于需求规格说明书。 1&#xff09;等价类&#xff1a;有效等价类、无效等价类 2&#xff09;边界值 3&#xff09;因果图&#xff1a;不同的原因对应…

C/C+=内存管理

C/C内存管理以及动态内存的申请_c动态内存的申请与释放_Demo Test的博客-CSDN博客 问题是&#xff0c;这个0x0804 8000 到0xC 0000 0000之间&#xff0c;不止3GB&#xff0c;应该有47GB&#xff0c;该怎么解释呢&#xff1f;

【图像分类】【深度学习】【Pytorch版本】ResNet模型算法详解

【图像分类】【深度学习】【Pytorch版本】 ResNet模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】 ResNet模型算法详解前言ResNet讲解Deep residual learning framework(深度残差学习框架)ResNet残差结构ResNet模型结构 ResNet Pytorch代码完整代码总结 前言 …

深入探讨TensorFlow:张量与矩阵

在机器学习和深度学习领域中&#xff0c;TensorFlow作为一款强大且受欢迎的开源机器学习框架&#xff0c;为研究人员和开发者提供了丰富的工具和资源。在TensorFlow中&#xff0c;张量&#xff08;tensor&#xff09;和矩阵&#xff08;matrix&#xff09;是核心概念&#xff0…

Odoo 15开发手册第七章 记录集 - 使用模型数据

在前面的章节中&#xff0c;我们概览了模型创建以及如何向模型加载数据。现在我们已有数据模型和相关数据&#xff0c;是时候学习如何编程与其进行交互了。 业务应用需要业务逻辑来计算数据、执行验证或自动化操作。Odoo框架API为开发者提供了工具用于实现这种业务逻辑。大多数…

(二)什么是Vite——Vite 和 Webpack 区别(冷启动)

vite分享ppt&#xff0c;感兴趣的可以下载&#xff1a; ​​​​​​​Vite分享、原理介绍ppt 什么是vite系列目录&#xff1a; &#xff08;一&#xff09;什么是Vite——vite介绍与使用-CSDN博客 &#xff08;二&#xff09;什么是Vite——Vite 和 Webpack 区别&#xff0…

2018年五一杯数学建模C题江苏省本科教育质量综合评价解题全过程文档及程序

2019年五一杯数学建模 C题 江苏省本科教育质量综合评价 原题再现 随着中国的改革开放&#xff0c;国家的综合实力不断增强&#xff0c;中国高等教育发展整体已进入世界中上水平。作为一个教育大省&#xff0c;江苏省的本科教育发展在全国名列前茅&#xff0c;而江苏省13个地级…

【PB续命05】WinHttp.WinHttpRequest的介绍与使用

0 WinHttp.WinHttpRequest简介 winhttp.winhttprequest是Windows操作系统中的一个API函数&#xff0c;用于创建和发送HTTP请求。它可以用于从Web服务器获取数据&#xff0c;或将数据发送到Web服务器。该函数提供了许多选项&#xff0c;例如设置请求头、设置代理服务器、设置超…

花 200 元测试 1300 个实时数据同步任务

背景 对于将数据作为重要生产资料的公司来说&#xff0c;超大规模的数据迁移同步系统( 1k、5k、10k 条同步任务)是刚需。 本文以此为出发点&#xff0c;介绍近期 CloudCanal 所做的一个容量测试&#xff1a;在单个 CloudCanal 集群上创建 1300 实时任务&#xff0c;验证系统是…

2023年中国骨质疏松治疗仪发展趋势分析:小型且智能将成为产品优化方向[图]

骨质疏松治疗仪利用磁场镇静止痛、消肿消炎的治疗作用迅速缓解患者腰背疼痛等骨质疏松临床症状。同时利用磁场的磁-电效应产生的感生电势和感生电流&#xff0c;改善骨的代谢和骨重建&#xff0c;通过抑制破骨细胞、促进成骨细胞的活性来阻止骨量丢失、提高骨密度。 骨质疏松治…

【软件推荐】我的常用Windows软件

文章目录 前言Colors Lite&#xff08;颜色吸取&#xff09;Everything&#xff08;文件搜索&#xff09;知云文献翻译Directory Opus&#xff08;文件管理器&#xff09;Snipaste&#xff08;截图&#xff09;AxMath&#xff08;公式编辑器&#xff09;Deskpin&#xff08;窗口…