【STL深入浅出】之string类的简单模拟实现

在这里插入图片描述

📃博客主页: 小镇敲码人
💚代码仓库,欢迎访问
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞

【C++STL】string类的简单模拟实现

  • 🚏 string类的成员变量及相关函数设计
  • 🚏 string类的构造函数的实现
    • ⛽️ 无参构造函数
    • ⛽️ string类的析构函数的实现
    • ⛽️ string类的其它构造函数
      • 🚀 赋值运算符重载函数
      • 🚀 拷贝构造函数和其它构造函数
  • 🚏 string类迭代器的实现
  • 🚏 string类Capacity函数的实现
    • ⛽️ 较为基础的几个函数
    • ⛽️ clear清除函数
    • ⛽️ resize函数
    • ⛽️ shrink_to_fit 函数
  • 🚏 string类访问操作符的实现
  • 🚏 string类Modifiers函数的实现
    • ⛽️ operator+=函数
    • ⛽️ append函数
    • ⛽️ insert函数
    • ⛽️ push_back函数
  • 🚏 string类operations
    • ⛽️ c_str函数
    • ⛽️ substr函数
    • ⛽️ find函数
  • 🚏 非成员函数的实现
    • ⛽️ operator+系列
  • 🚏 测试函数

前言:本篇博客用到的C语言字符串操作函数和内存函数比较多,可以看一下博主这篇博客内存函数、字符串函数。代码仓库自取

🚏 string类的成员变量及相关函数设计

最重要的私有的成员变量只有三个:char*用来存字符串、size_保存有效字符的个数、capacity_表示当前申请的空间的大小。还有一个是类公有的静态成员常数npos,我们先照着文档和源码把string类的一些函数接口和类型敲出来:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<stack>
#include<assert.h>using namespace std;namespace my_string
{class string{public:typedef char* iterator;//定义普通的正向迭代器类型typedef const char* const_iterator;//定义const的正向迭代器类型iterator begin(){return str_;//返回字符串首地址即可}iterator end(){return str_+size_;//返回字符串终止字符的地址即可}const_iterator begin() const{return str_;}const_iterator end() const{return str_+size_;}string()//无参数的构造函数{}string(const string& str);//拷贝构造函数string(const char* s);//使用c字符串构造string(size_t n, char c);//使用n个字符c来构造string类size_t size() const{return size_;}size_t length() const{return size_;}size_t capacity() const{return capacity_;}void resize(size_t n, char c = '\0');void shrink_to_fit();void clear();char& operator[] (size_t pos);const char& operator[] (size_t pos) const;string& operator+= (const string& str);string & operator+= (const char* s);string& operator+= (char c);string& append(const string& str);string& append(const char* s);string& append(size_t n, char c);string& insert(size_t pos, const string& str);iterator insert(iterator p, char c);void push_back(char c);iterator erase(iterator p);string& erase(size_t pos = 0, size_t len = npos);const char* c_str() const{return this->str_;}size_t find(const char* str, size_t pos = 0);size_t find(const string& s, size_t pos = 0);string substr(size_t pos = 0, size_t len = npos);void swap(string& s);string& operator= (const string& str);void swap(string& x, string& y);~string();static const size_t npos = -1;private:char* str_ = nullptr;//保存字符串的个数,给缺省值size_t size_ = 0;size_t capacity_ = 0;};istream& operator>> (istream& is, string& str);ostream& operator<< (ostream& os, const string& str);string operator+ (const string& lhs, const string& rhs);string operator+ (const string & lhs, const char* rhs);string operator+ (const char* lhs, const string& rhs);string operator+ (const string& lhs, char          rhs);string operator+ (char          lhs, const string& rhs);istream& getline(istream& is, string& str);void swap(string& x, string& y);void Test_string1();//测试函数
}

类的分文件编程,较短的函数可以在头文件里面直接实现,类里面的函数默认是隐式内联函数。但分文件编程,其它非内联函数要老老实实定义和实现分离。

类的份文件编程,要在.cpp文件的函数名前面加上对于的类名,因为类对它的函数有类域的限制和命名空间类似,同样的如果你之前整个类在一个命名空间里声明,在.cpp文件中同样要加上那命名空间,或者是打开那个命名空间

🚏 string类的构造函数的实现

⛽️ 无参构造函数

string()//无参数的构造函数{}

⛽️ string类的析构函数的实现

直接释放空间即可。

	string::~string(){size_ = 0;capacity_ = 0;delete[] str_;str_ = nullptr;}

⛽️ string类的其它构造函数

🚀 赋值运算符重载函数

我们实现了1个版本:
在这里插入图片描述

这里我们使用了现代版本,去复用刚刚实现的拷贝构造函数(深拷贝),再把深拷贝出来的类和我们自己的类交换一下内容即可,效率上相差不大,因为我们没有使用引用计数的方式,深拷贝是避不开的。

代码实现:

string& string::operator= (const string& str){string tmp(str);tmp.swap(*this);return *this;}

🚀 拷贝构造函数和其它构造函数

在这里插入图片描述
1.拷贝构造函数。

	string::string(const string& str)//拷贝构造函数{str_ = new char[str.capacity_+1];//给`\0`多开一个空间strcpy(str_, str.str_);capacity_ = str.capacity_;size_ = str.size_;}

2.其它两个构造函数的实现:

   string::string(const char* s)//使用c字符串构造{size_ = strlen(s);capacity_ = size_;str_ = new char[capacity_ + 1];memcpy(str_, s, size_ + 1);//\0也要拷贝进去}string::string(size_t n, char c)//使用n个字符c来构造string类{size_ = n;capacity_ = size_;str_ = new char[capacity_ + 1];//给\0预留一个空间memset(str_, c, n);//逐字节初始化str_[size_] = '\0';}

🚏 string类迭代器的实现

string类的迭代器比较简单,不需要重新创建一个类,当然如果我们想要实现反向迭代器的话需要自己重新实现一个迭代器的类,这里我们为了省事,就直接用原生指针代替了,因为string类型本质是使用字符数组存储的,空间连续,++和–操作不需要我们重新来设计。

        typedef char* iterator;//定义普通的正向迭代器类型typedef const char* const_iterator;//定义const的正向迭代器类型iterator begin(){return str_;//返回字符串首地址即可}iterator end(){return str_+size_;//返回字符串终止字符的地址即可}const_iterator begin() const{return str_;}const_iterator end() const{return str_+size_;}

const迭代器不能修改其指向的内容,直接加上const即可。

🚏 string类Capacity函数的实现

在这里插入图片描述

这几个函数我们只实现了一部分,因为有些比较简单亦或是比较困难,我们自己手撕string是为了更好的了解这个类。

⛽️ 较为基础的几个函数

const是为了防止这些string类的变量被修改。

      size_t size() const{return size_;}size_t length() const{return size_;}size_t capacity() const{return capacity_;}

⛽️ clear清除函数

这个函数没有清理空间,仅仅将size_置为了0。

	void string::clear(){size_ = 0;str_[0] = '\0';}

⛽️ resize函数

这个函数的功能我们上一篇博客已经介绍过了,就是改变类的size_,同时可能会给其初始化,但是只能初始化原先没有存在的位置,可能会存在扩容,但是不会缩容。
在这里插入图片描述
实现代码:

void string::resize(size_t n, char c){if (n > size_){//先考虑是否扩容if (n > capacity_)//需要扩容{char* tmp = new char[n+1];memcpy(tmp, str_,size_);//把之前的内容拷贝进来memset(tmp + size_, c, n - size_);//初始化其余的空间size_ = n;capacity_ = n;tmp[size_] = '\0';delete[] str_;//把之前空间的内存释放掉str_ = tmp;}else//不需要扩容,直接初始化后面空间的内容即可{memset(str_ + size_,c, n - size_);size_ = n;str_[size_] = '\0';}}else//n < size_,只需要改size_和\0{size_ = n;str_[size_] = '\0';}}

⛽️ shrink_to_fit 函数

库里面的这个函数是用来让size_和capacity_的值保持基本一致的,但是库里面的容量的值基本都定了,所以有时候当我们的size_capacity接近时是不会缩容的,但是我们自己实现就不用这么细致了。

直接缩容就完事。

	void string::shrink_to_fit(){char* tmp = new char[size_ + 1];capacity_ = size_;memcpy(tmp, str_,size_ + 1);//把原先的字符串(包括\0)都拷贝给tmpdelete[] str_;//释放原来空间的内存str_ = tmp;}

``

🚏 string类访问操作符的实现

operator[]函数的实现,我们实现了const非const两个版本。

在这里插入图片描述
代码实现

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

at函数需要抛异常,其实和operator[]函数类似,只不过at函数的检查更严格,这里我们不再实现。

🚏 string类Modifiers函数的实现

在这里插入图片描述

⛽️ operator+=函数

这个函数和append追加函数类似,都是在字符串结尾添加内容,本质上就是一些内容的拷贝,代码不难理解,如果重新开空间不要忘了将原先的内容拷贝进来,并释放空间。

我们实现了三个版本:

在这里插入图片描述

string& string::operator+= (const string& str){//看是否需要扩容if (str.size() + size_ > capacity_)//需要{char* tmp = new char[str.size() + size_ + 1];//保存之前的字符串if(str_)memcpy(tmp, str_, size_);if(str.str_)memcpy(tmp+size_, str.str_, str.size_+1); //str(包括\0)都拷贝给tmp//更新成员变量size_ += str.size();capacity_ = size_;delete[] str_;//释放之前申请的的空间str_ = tmp;}else//不需要{memcpy(str_ + size_, str.str_, str.size_+1);//把\0也copy进去size_ += str.size();str_[size_] = '\0';}return *this;}string& string::operator+= (const char* s){assert(s != NULL);int length = strlen(s);//看是否需要扩容if (length + size_ > capacity_)//需要{char* tmp = new char[size_ + length + 1];//保存之前的字符串if(str_ != nullptr)memcpy(tmp,str_, size_);memcpy(tmp + size_,s, length + 1);字符串s(包括\0)都拷贝给tmp//更新成员变量size_ += length ;capacity_ = size_;delete[] str_;//释放之前申请的的空间str_ = tmp;}else//不需要{memcpy(str_ + size_,s, length + 1);//把\0也copy进去size_ += length;}return *this;}string& string::operator+= (char c){//先看是否需要扩容if (size_ + 1 > capacity_){capacity_ = capacity_ == 0 ? 4 : capacity_ * 1.5;//扩容到原先的1.5倍char* tmp = new char[capacity_ + 1];if(str_ != nullptr)//不能把空地址的内容拷贝到tmp中strcpy(tmp,str_);tmp[size_++] = c;tmp[size_] = '\0';if(str_ != nullptr)delete[] str_;str_ = tmp;}else{str_[size_++] = c;str_[size_] = '\0';}return *this;}

strcpy函数在这里和memcpy没有太大区别,但是内存拷贝函数memcpy函数的应用范围更广。

⛽️ append函数

这个函数和operator+=函数没有声明太大区别,都是字符串追加函数,我们同样实现了三个版本,对operator+=函数进行了复用。

	string& string::append(const string& str)//{*this += str;return *this;}string& string::append(const char* s){*this += s;return *this;}string& string::append(size_t n, char c){string tmp(n,c);*this += tmp;return *this;}

⛽️ insert函数

这个插入函数我们只实现了两个版本。
在这里插入图片描述
在某个位置之前插入string类和在某个迭代器前插入一个字符:

string& string::insert(size_t pos, const string& str){//是否需要扩容if (size_ + str.size() > capacity_){capacity_ = size_ + str.size();//更新capacity_char* tmp = new char[capacity_ + 1];//重新申请空间strcpy(tmp,str_);//把之前的内容拷贝进来先把pos位置及其之后的字符后移memcpy(tmp + pos+str.size(), tmp + pos, size_-pos+1);//把\0也拷贝进去memcpy(tmp + pos, str.str_, str.size());delete[] str_;str_ = tmp;size_ += str.size();}else{size_ += str.size();//先把pos位置及其之后的字符后移memcpy(str_ + pos + str.size(),str_ + pos,size_ - pos + 1);//把\0也拷贝进去memcpy(str_ + pos, str.str_, str.size());}return *this;}string::iterator string::insert(iterator p, char c){//需要扩容int pos_ = p - begin();//提前保存,防止迭代器失效if (size_ == capacity_){capacity_ = capacity_ == 0 ? 4 : capacity_ * 1.5;char* tmp = new char[capacity_+1];strcpy(tmp, str_);int pos = pos_;for (int i = size_; i >= pos; --i)//包括\0都后移一位tmp[i + 1] = tmp[i];tmp[pos] = c;delete[] str_;str_ = tmp;size_++;}else{int pos = pos_;for (int i = size_; i >= pos; --i)//包括\0都后移一位str_[i + 1] = str_[i];size_++;}return str_+pos_;}

对第一个不是迭代器版本的关键代码做一下解释:
在这里插入图片描述

⛽️ push_back函数

在字符串结尾尾插一个字符,实现价值不大。但我们还是实现了一下,复用了一下insert函数:

	void string::push_back(char c){insert(end(), c);}

🚏 string类operations

在这里插入图片描述
这上面的函数我们只实现了比较重要的几个:

⛽️ c_str函数

返回c字符串,比较简单:

	const char* c_str() const{return this->str_;}

因为这是string类的字符串,所以返回值是const类型防止你随便修改。

⛽️ substr函数

这个函数的功能我们再上篇博客已经谈到了,这里主要阐述一下这个string切割函数应该如何实现。
实现很简单,分为两种情况即可,
1、len的长度大于pos后面的字符,这种情况把后面的字符都copy给我们新开的字符数组
2、len的长度小于剩余的字符长度,copy对应长度即可。

代码实现:

   string string::substr(size_t pos, size_t len){string s;if (len == string::npos || pos+len > size_)//如果{char* tmp = new char[size_ - pos + 1];//给\0也要开空间memcpy(tmp, str_ + pos, size_ - pos + 1);//把pos及其后面的字符复制给tmps = tmp;//赋值}else{char* tmp = new char[len + 1];memcpy(tmp, str_ + pos, len);tmp[len] = '\0';s = tmp;}return s;}

⛽️ find函数

我们只实现了两个版本,复用了一下substr函数,并使用了字符串比较函数strcmp,逻辑比较简单,就是简单的遍历。

在这里插入图片描述代码实现:

    size_t string::find(const char* str, size_t pos){int len = strlen(str);//计算出str字符串的长度int pos_ = -1;for (int i = pos; i < size_ - len; ++i)//从pos位置开始遍历,找接下来的子串是否含有str{string tmp = substr(i, len);//复用string切割函数if (strcmp(tmp.str_, str) == 0)//字符串比较函数来比较{pos_ = i;break;}}return pos_ == -1 ? string::npos : pos_;//如果没有找到,就返回npos}size_t string::find(const string& s, size_t pos){int pos_ = -1;//为-1,代表不存在for (int i = pos; i < size_ - s.size(); ++i)//从pos开始遍历找{string tmp = substr(i,s.size());if (strcmp(s.str_,tmp.str_) == 0)//比较{pos_ = i;//找到了返回起始位置,breakbreak;}}return pos_ == -1 ? string::npos : pos_;}

🚏 非成员函数的实现

⛽️ operator+系列

这个函数是运算符+的重载,我们可以复用operator+=函数。
在这里插入图片描述

代码实现:

   string operator+ (const string& lhs, const string& rhs){string tmp(lhs);//拷贝构造tmp += rhs;//赋值运算符重载return tmp;}string operator+ (const string& lhs, const char* rhs){string tmp(lhs);//拷贝构造tmp += rhs;//赋值运算符重载return tmp;}string operator+ (const char* lhs, const string& rhs){string tmp(lhs);//拷贝构造tmp += rhs;//赋值运算符重载return tmp;}string operator+ (const string& lhs, char   rhs){string tmp(lhs);//拷贝构造tmp += rhs;//赋值运算符重载return tmp;}string operator+ (char lhs, const string& rhs){string tmp(1, lhs);//调用构造函数tmp += rhs;//赋值运算符重载return tmp;}

注意:这里返回值不能传引用,因为tmp出来作用域就销毁了。

🚏 测试函数

void Test_string1()//测试迭代器和构造函数以及赋值运算符重载{string s1;//默认构造string s2("520");string s3(s2);//拷贝构造string s4(4, 'a');cout << "s1: " << s1 << "s2: " << s2 << " s3: " << s3 << " s4:" << s4 << endl;//<< 操作符重载s1 = s3;//赋值运算符cout << "s1: " << s1 << "s2:" << s2 << " s3: " << s3 << " s4:" << s4 << endl;//<< 操作符重载cin >> s1;cout << "s1: " << s1 << "s2:" << s2 << " s3: " << s3 << " s4:" << s4 << endl;//<< 操作符重载string s5;//+=操作符重载s5 += '1';s5 += "314";s5 += s2;cout << "s5: " << s5 << endl;//+操作符重载string firstlevel("com");string secondlevel("cplusplus");string scheme("http://");string hostname;string url;hostname = "www." + secondlevel + '.' + firstlevel;url = scheme + hostname;cout << "url: " << url << '\n';//insert、erase函数string s6("1520");string s7("314");s6.insert(1, s7);cout << "s6: " << s6 << endl;s6.insert(s6.begin(), 'w');cout << "s6: " << s6 << endl;s6.erase(s6.begin());cout << "s6: " << s6 << endl;s6.erase(s6.end());cout << "s6: " << s6 << endl;s6.erase(0, 4);cout << s6 << endl;//find函数string s8("xxxxxlovexxxxxlovexxxxlove");string str1("lovexxxxxx");const char* str2 = "xxl";size_t pos1 = s8.find(str1, 0);//从下标7开始往后找if (pos1 != string::npos){cout << "找到了!!!!首次出现的位置是" << pos1 << endl;}else{cout << "没有找到" << endl;}size_t pos2 = s8.find(str2, 0);//从下标7开始往后找if (pos2 != string::npos){cout << "找到了!!!!位置是" << pos2 << endl;}else{cout << "没有找到" << endl;}//substrstring s9("love and help");cout << "s9:" << s9.substr(9, 4) << endl;//从s的下标9位置开始的连续4个字符,help//append函数string str;string str4("Writing ");string str5("print 10 and then 5 more");// used in the same order as described above:str.append(str4);                       // "Writing "str.append("here: ");                   // "here: "str.append(10u, '.');                    // ".........."cout << "str:" << str << '\n';//swap函数cout << "s1: " << s1 << " s2: " << s2 << endl;swap(s1, s2);cout << "s1: " << s1 << " s2: " << s2 << endl;//resize、shrink_to_fitstring s10 = "reserve";cout << "size: " << s10.size() << "capacity: " << s10.capacity() << endl;s10.shrink_to_fit();cout << "size: " << s10.size() << "capacity: " << s10.capacity() << endl;s10.resize(1000, '5');cout << "size: " << s10.size() << "capacity: " << s10.capacity() << endl;s10.shrink_to_fit();cout << "size: " << s10.size() << "capacity: " << s10.capacity() << endl;//size、lengthstring s11 = "xxxxxx";cout << "size: " << s11.size() << endl;cout << "length: " << s11.length() << endl;//capacitystring  s12("xxxxxxxxxxxxx");int pre = s12.capacity();for (int i = 0; i < 100000; i++){s12 += 'x';if (pre < s12.capacity()){cout << "i: " << i << "capacity: " << "增加的字节倍数:" << 1.0 * s12.capacity() / pre << " " << s12.capacity() << endl;pre = s12.capacity();}}//clear函数s12.clear();cout << "s12: " << s12 << endl;}

运行结果:
在这里插入图片描述

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

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

相关文章

“慢公司”小红书,加速布局大模型

自成立至今&#xff0c;小红书一直保持低调并被形容为“慢公司”&#xff0c;而这也反映在大模型相关产品线上。尽管上线了多项功能和应用&#xff0c;存在感却稍显不足。 进入2024年&#xff0c;小红书在大模型领域的探索悄然加速&#xff0c;持续引发市场关注度。 多方消息…

BIOS主板(非UEFI)安装fedora40的方法

BIOS主板(非UEFI)安装fedora40的方法 现实困难&#xff1a;将Fedora-Workstation-Live-x86_64-40-1.14.iso写入U盘制作成可启动U盘启动fedora40&#xff0c;按照向导将fedora40安装到真机的sda7分区中得到报错如下内容&#xff1a; Failed to find a suitable stage1 device: E…

socket.gaierror: [Errno 11001] getaddrinfo failed

socket.gaierror: [Errno 11001] getaddrinfo failed 错误通常表示在解析主机名或 IP 地址时出现问题。这可能是因为提供的 IP 地址或主机名无效&#xff0c;或者存在 DNS 解析问题。 打印日志发现config.ini中的host不能加引号

Javaweb基础之json

大家好&#xff0c;这里是教授.F 目录 引入&#xff1a; 定义格式&#xff1a; json规制&#xff1a; 字符串转json&#xff1a; json转字符串: 字符串和json转化细节&#xff1a; json在java中的使用: 应用实例&#xff1a; JavaBean和json字符串的转换&#xff1a; l…

ZL-GL-4离体组织灌流系统测试在恒温条件下离体标本的肌张拉力

简单介绍&#xff1a; 离体组织灌流系统为生理实验及药理实验提供恒温环境&#xff0c;在麦氏浴皿内加养液同时能通氧&#xff0c;测试在恒温条件下离体标本的肌张拉力&#xff0c;离体组织灌流系统具有进气口,配备微调固定器,省时省力,并提高了实验效率,同时可方便串联恒温供水…

prometheus+alertmanager+webhook钉钉机器人告警

版本&#xff1a;centos7.9 python3.9.5 alertmanager0.25.0 prometheus2.46.0 安装alertmanager prometheus 配置webhook # 解压&#xff1a; tar -xvf alertmanager-0.25.0.linux-amd64.tar.gz tar -xvf prometheus-2.46.0.linux-amd64.tar.gz mv alertmanager-0.25.0.linu…

C/S模型测试及优化

1 1.1代码示例 #include<stdio.h> #include<stdio.h>#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>#include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.…

计算机基础知识

计算机基础知识 计算机是如何工作的 在本节内容中,介绍了计算机核心工作机制,计算机结构,框架等,计算机需求在人类的历史中是广泛存在的,发展大体经历了从一般计算工具到机械计算机到目前的电子计算的发展过程 文章目录 计算机基础知识一、冯诺依曼体系结构二、CPUCPU的核心参…

STM 32_HAL_SDIO_SD卡

STM32的SDIO&#xff08;Secure Digital Input Output&#xff09; 接口是一种用于SD卡和MMC卡的高速数据传输接口。它允许STM32微控制器与多种存储卡和外设进行通信&#xff0c;支持多媒体卡&#xff08;MMC卡&#xff09;、SD存储卡、SDI/O卡和CE-ATA设备。STM32的SDIO控制器…

JAVA-学习

一、垃圾回收机制 1、为什么要进行垃圾回收机制 如果不进行垃圾回收&#xff0c;内存迟早都会被消耗空&#xff0c;因为我们在不断的分配内存空间而不进行回收。除非内存无限大&#xff0c;我们可以任性的分配而不回收&#xff0c;但是事实并非如此。所以&#xff0c;垃圾回收…

【大模型】在大语言模型的架构中,Transformer有何作用?

Transformer在大语言模型架构中的作用 Transformer是一种用于序列到序列&#xff08;Seq2Seq&#xff09;任务的深度学习模型&#xff0c;由Vaswani等人于2017年提出。在大语言模型&#xff08;LLM&#xff09;的架构中&#xff0c;Transformer扮演着关键的角色&#xff0c;它…

Flink中因java的泛型擦除导致的报错及解决

【报错】 Exception in thread "main" org.apache.flink.api.common.functions.InvalidTypesException: The return type of function Custom Source could not be determined automatically, due to type erasure. You can give type information hints by using th…

【php实战项目训练】——thinkPhP的登录与退出功能的实现,让登录退出畅通无阻

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

Mendix 创客访谈录|Mendix助力开发高手10日交付复杂应用,且支持移动端呈现

本期创客 莊秉勳 布鲁科技技术顾问 各位Mendix社群的夥伴好&#xff0c;我是莊秉勳&#xff0c;大家也可以叫我Danny。 我大學是資訊科學背景&#xff0c;在與Mendix相遇前&#xff0c;曾在一上市製造企業&#xff0c;擔任軟體工程師&#xff0c;負責企業內部軟體開發&#xf…

Virtualbox 安装unbuntu + qemu

0. 前言 关于 Virualbox 安装虚拟机的优秀文章太多了&#xff0c;笔者主要是着重梳理一些安装小细节&#xff0c;利己利人&#xff01;&#xff01; 如果需要保姆式的安装教程&#xff0c;可以查看后续的参考链接。 1. VirtualBox 的安装 直接去官网搜索最近的软件即可&…

js每日十题(二)

1. 6.3 js第6题 以下结语句中&#xff0c;返回true的是&#xff1f; A !![] B 1’1’ C nullundefined D !!’’ 答&#xff1a; A选项&#xff0c;由于数组属于对象类型&#xff0c;所以空的数组转换成布尔型是true&#xff0c;前置&#xff01;&#xff01;&#xff0c;两次取…

【paper】环形虚拟管内的多无人机协同目标包围

Multi-UAV cooperative target encirclement within an annular virtual tube2022.8ELSEVIER Aerospace Science and Technology【Q1 5.6】Yan Gao 全权 北航 Q1 Background&#xff1a;本文试图解决一个什么样的问题&#xff1f; 多无人机对单个静态目标进行连续包围任务&…

基于聚类和回归分析方法探究蓝莓产量影响因素与预测模型研究附录

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 目录 背景数据说明数据来源思考 附录数据预处理导入包以及数据读取数据预览数据处理 相关性分析聚类分析数据处理确定聚类数建立k均值聚类模型 …

12- Redis 中的 链表 数据结构

Redis 的 List 对象的底层实现之一就是链表。C 语言本身没有链表这个数据结构&#xff0c;所以 Redis 自己设计了一个链表数据结构。 1. 链表节点结构设计 先来看看【链表节点】结构的样子&#xff1a; typedef struct listNode {//前置节点struct listNode *prev;//后置节点…

liunx配置网络的命令

liunx配置网络的命令 文章目录 liunx配置网络的命令ifconfig命令查看路由表信息netstat命令ss命令lsof命令ping 命令nslookup命令 ifconfig命令 ifconfig:显示正在工作的网卡&#xff0c;启动的设备 ifconfig -a 展示所有设备 ens33: flags4163<UP,BROADCAST,RUNNING,MUL…