c_str(cpp中使用了string,要和c语言兼容,用c去读文件等)
string模拟实现
构造函数+析构函数
注意:1.空对象初始化时开一个空间:\0
2.写全缺省,隐含了一个\0,只要是常量字符串,后面都已\0为结尾
- /*string() //无参构造函数
- :_str(new char[1])
- ,_size(0)
- ,_capacity(0)
- {
- _str[0] = '\0';
- }*/
-
- //string(const char* str = "")//strlen时间复杂度O(N)
- // :_str(new char[strlen(str)+1])
- // ,_size(strlen(str))
- // , _capacity(strlen(str))//capacity不包含\0,只统计有效字符
- //{
- // strcpy(_str, str);
- //}
-
- string(const char* str = "")//全缺省,隐含了一个\0,只要是常量字符串,后面都已\0为结尾
- {
- _size = strlen(str);
- _capacity = _size;
- _str = new char[_capacity + 1];
- strcpy(_str, str);
- }
-
- ~string()
- {
- delete[] _str;
- _str = nullptr;
- _size = _capacity = 0;
- }
拷贝构造函数:深拷贝
默认的拷贝构造,代码崩溃原因:浅拷贝指向同一块空间并析构两次(默认生成拷贝构造对内置类型值拷贝)
- void test_string2()
- {
- string s1("hello world");
- string s2(s1);
- cout << s1.c_str() << endl;
- cout << s2.c_str() << endl;
-
- }
正确的普通写法
- string(const string& s)//s2(s1)
- :_str(new char[s._capacity+1])//capacity存储有效数据空间,每次都多开一个\0
- ,_size(s._size)
- ,_capacity(s._capacity)
- {
- strcpy(_str, s._str);
- }
正确的现代写法 --利用构造函数swap
把随机数换给tmp导致程序崩溃--析构释放随机空间
- string(const string& s)//s2(s1)
- {
- string tmp(s._str);//tmp是打工人,s._str是const字符串,相当于调用构造函数,s2想要一样大的空间和一样大的值
- swap(_str, tmp._str);
- swap(_size, tmp._size);
- swap(_capacity, tmp._capacity);
- }
再改进:放nullptr,delete nullptr不会报错
- void swap(string& tmp)
- {
- ::swap(_str, tmp._str);//::调用全局swap
- ::swap(_size, tmp._size);
- ::swap(_capacity, tmp._capacity);
- }
-
- string(const string& s)//s2(s1)
- :_str(nullptr)
- ,_size(0)
- ,_capacity(0)
- {
- string tmp(s._str);//tmp是打工人,s._str是const字符串,相当于调用构造函数
- swap(tmp);//s2和tmp交换
- }
赋值运算符拷贝
默认的赋值代码崩溃原因:同样的浅拷贝报错,只是依次拷贝给s1,内存泄漏+析构两次
- void test_string3()
- {
- string s1("hello world");
- string s3("ww");
- s1 = s3;
- }
正确的普通写法(直接释放旧空间重新开辟)
自己给自己赋值(释放后随机值拷贝给自己)解决方法:判断相等
- string& operator=(const string& s)
- {
- if (this != &s)
- {
- char* tmp = new char[s._capacity + 1];
- //先开空间再释放,防止new失败抛异常破坏原数据
- strcpy(tmp, s._str);
- delete[] _str;
- _str = tmp;
- _size = s._size;
- _capacity = s._capacity;
- }
- return *this;
- }
现代写法
1.错误的写法,此处swap包含赋值,死循环
- string& operator=(const string& s)
- {
- if (this != &s)
- {
- string tmp(s);//调拷贝构造
- ::swap(*this,tmp);
- }
- return *this;
- }
2.直接使用全局中的swap包含了一次拷贝构造,两次赋值,一共三次深拷贝,代价极高

正确的写法,swap成员变量换
- void swap(string& tmp)
- {
- ::swap(_str, tmp._str);//::调用全局swap
- ::swap(_size, tmp._size);
- ::swap(_capacity, tmp._capacity);
- }
-
- string& operator=(const string& s)
- {
- if (this != &s)
- {
- //string tmp(s._str);//调用构造函数
- string tmp(s);//调拷贝构造
- swap(tmp); //this->swap(tmp)
- }
- return *this;
- }
第二种正确的写法
- string& operator=(string s)//传值拷贝顶替tmp,s就是深拷贝的s3
- {
- swap(s);
- return *this;
- }
resize和reserve
resize默认用0来填充多出的元素空间,resize改变size大小,开空间+初始化。resize新的字符原有的值不动,在后面新增字符。resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变
- void resize(size_t n, char ch = '\0')
- {
- if (n > _size)//插入数据
- {
- reserve(n);
- for (size_t i = _size; i < n; ++i)
- {
- _str[i] = ch;
- }
- _str[n] = '\0';
- _size = n;
- }
- else//删除数据
- {
- _str[n] = '\0';
- _size = n;
- }
- }
reserve不改变有效元素个数,只开空间。当reserve的参数小于string的底层空间总大小时,不会改变容量大小,比当前空间大完成扩容
- void reserve(size_t n)
- {
- if (n > _capacity)
- {
- char* tmp = new char[n+1];
- strcpy(tmp, _str);
- delete[] _str;
- _str = tmp;
- _capacity = n;
- }
- }
push_back && append
- 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);//strcpy也会拷贝\0
- _size += len;
- }
-
- string& operator+=(char ch)
- {
- push_back(ch);
- return *this;
- }
-
- string& operator+=(const char* str)
- {
- append(str);
- return *this;
- }
insert
注意边界
- string& 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];//防止pos位置为0时--end越界
- --end;
- }
- _str[pos] = ch;
- ++_size;
- return *this;
- }
-
- 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 end = _size + len;
- while (end >= pos + len)
- {
- _str[end] = _str[end - len];
- --end;
- }
- strncpy(_str + pos, str, len);
- _size += len;
- return *this;
- }
erase
- void erase(size_t pos, size_t len = npos)
- {
- assert(pos < _size);
- if (len == npos || pos + len >= _size)//>=size删完
- {
- _str[pos] = '\0';
- _size = pos;
- }
- else//删除需要挪动数据,挪动pos+len往前覆盖
- {
- strcpy(_str + pos, _str + pos + len);
- _size -= len;
- }
- }
<< && >>
注意:空格和换行默认作为数据之间的间隔,in>>ch获取会忽略掉空格或者换行,会认定为字符之间的换行输入、多个字符之间的空格或者换行
- ostream& operator<<(ostream& out, const string& s)
- {
- for (size_t i = 0; i < s.size(); ++i)
- {
- out << s[i];
- }
- return out;
- }
-
- void clear()//类里
- {
- _str[0] = '\0';
- _size = 0;
- }
-
- istream& operator>>(istream& in,string& s)
- {
- s.clear();//跟库中保持一致
- //char ch;
- //in >> ch;
- //while(ch != ' ' && ch != '\n')
- //{
- // s+= ch;
- // in >> ch;
- //}
- //拿不到空格和换行,程序无法终止,cin自动忽略空格或者换行,默认认为输入字符之间间隔
-
- char ch;
- ch = in.get();//get获取每一个对应字符,不会忽略\n或者空格
- const int N = 32;//类似缓冲区,避免多次重复扩容
- char buff[N];
- size_t i = 0;
- while(ch != ' ' && ch != '\n')
- {
- buff[i++] = ch;
- if (i == N - 1)
- {
- buff[i] = '\0';
- s += buff;
- i = 0;
- }
- ch = in.get();
- }
- buff[i] = '\0';
- s += buff;
-
- return in;
- }
迭代器
所有迭代器区间都是左闭右开;迭代器用法类似;迭代器用法类似指针
迭代器需要begin(返回第一个位置的类似用法指针)和end(最后一个位置的下一个位置)
- void test_string2()
- {
- string s1("hello");
- //普通用法
- for (size_t i = 0; i < s1.size(); ++i)
- {
- cout << s1[i] << " ";
- }
-
- //string迭代器写法
- string::iterator it = s1.begin();
- while (it != s1.end())
- {
- cout << *it;
- it++;
- }
- cout << endl;
-
- //list迭代器用法
- list<int> lt(10, 1);
- list<int>::iterator lit = lt.begin();
- while (lit != lt.end())
- {
- cout << *lit<< " ";
- ++lit;
- }
-
- }
迭代器模拟实现
string的迭代器就是原生指针
- namespace MyString
- {
- class string
- {
-
- public:
- typedef char* iterator;
- iterator begin()
- {
- return _str;//返回第一个位置
- }
- iterator end()
- {
- return _str+_size;//end返回的迭代器是最后一个数据的下一个位置(\0)
- }
-
- void test_string1()
- {
- string s1("hello world");
- string s2;
- /*for (size_t i = 0; i < s1.size(); ++i)
- {
- cout << s1[i];
- }*/
-
- string::iterator it = s1.begin();
- while (it != s1.end())
- {
- cout << *it << " ";
- it++;
- }
- cout << endl;
- for (auto ch : s1)
- {
- cout << ch << " ";
- }
- }
-
- }
- }
范围for的底层就是迭代器,优化写法
c_str(cpp中使用了string,要和c语言兼容,用c去读文件等)

打印test.cpp中的数据读取到屏幕中
- void test_string5()
- {
- string filename("test.cpp");
- //用c去读文件
- FILE* fout = fopen(filename.c_str(), "r");
- assert(fout);
- char ch = fgetc(fout);
- while (ch != EOF)
- {
- cout << ch;
- ch = fgetc(fout);
- }
- }
-
-
- int main()
- {
- test_string5();
- return 0;
- }
注意,两个字符串打印有区别,c语言以\0为结尾.cpp以string对象的size为结尾
- cout << filename << endl;
- cout << filename.c_str() << endl;
append迭代器版本
- string s1("hello");
- string s2("world");
- s1.append(s2.begin(), s2.end());//左闭右开迭代器区间数据添加进去
- cout << s1;
find和substr
取文件后缀
- void test_string6()
- {
- string s("test.a.cpp");
- size_t pos = s.rfind('.');
- if (pos != string::npos)
- {
- string suf = s.substr(pos);
- cout << suf << endl;
- }
- }
取协议、网站域名、uri
- void DealUrl(const string& url)
- {
- size_t pos1 = url.find("://");
- if (pos1 == string::npos)
- {
- cout << "非法url" << endl;
- return;
- }
- string protocol = url.substr(0, pos1);
- cout << protocol<
-
- size_t pos2 = url.find('/', pos1 + 3);
- if (pos2 == string::npos)
- {
- cout << "非法url" << endl;
- return;
- }
- string domain = url.substr(pos1 + 3,pos2-pos1-3);
- cout << domain << endl;
-
- string uri = url.substr(pos2+1);
- cout << uri << endl;
- }
-
-
- int main()
- {
- string s1("https://www.baidu.com/baidu?tn=monline_3_dg&ie=utf-8&wd=%5C0%E7%AD%89%E4%BA%8E0");
- DealUrl(s1);
- return 0;
- }

注意:find是搜素字符串匹配字符的第一个位置,要求全部匹配;
find_first_of是匹配整个字符串中出现的对应字符
find模拟实现
-
- size_t find(char ch, size_t pos = 0)//找一个字符,从pos位置开始
- {
- assert(pos < _size);
- for (size_t i = pos; i < _size; ++i)
- {
- if (ch == _str[i])
- {
- return i;
- }
- }
- return npos;
- }
-
- size_t find(const char* sub, size_t pos = 0)
- {
- const char* tmp = strstr(_str + pos, sub);
- if (tmp == nullptr)
- {
- return npos;
- }
- else
- {
- return tmp - _str;//要返回下标,指针-指针
- }
- }
从pos位置开始,取len个字符构造新的字串:substr模拟实现
- string substr(size_t pos, size_t len = npos) const
- {
- assert(pos < _size);
- size_t reallen = len;
- if (len == npos || pos + len > _size)
- {
- reallen = _size - pos;//只要是左闭右开,右开-左闭就是长度
- }
- string tmp;
- for (size_t i = 0; i < reallen; ++i)
- {
- tmp += _str[pos+i];//剩余数据拷贝
- }
- return tmp;
- }
getline练习题
以下写法错误的原因在于cin
- #include
- #include
- using namespace std;
-
- int main(){
- string s;
- cin>>s;
- //getline(cin,s);
- size_t pos = s.rfind(' ');
- if(pos != string::npos)
- {
- cout<
size()-pos-1< - }
- else
- {
- cout<
size()< - }
- }
cin和scanf都一样,如果输入多个值或者多个字符串,多个值之间是用:空格,换行来间隔。
现在我们输入的一行里面,本身带有空格,用cin接收不到,只能接收到空格前的一串字符,剩下的字符在缓冲区中,此时使用getline

- #include
- #include
- using namespace std;
-
- int main(){
- string s;
- getline(cin,s);
- size_t pos = s.rfind(' ');
- if(pos != string::npos)
- {
- cout<
size()-pos-1< - }
- else
- {
- cout<
size()< - }
- }
运算符重载
- 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 strcmp(_str, s._str) == 0;
- }
-
- 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);
- }
to_string:从字符串转换-其他转字符串函数

- void test_string7()
- {
- const char* s1 = "123";
- cout<<atoi(s1);
-
- int ival;
- double dval;
- cin >> ival >> dval;
- string istr = to_string(ival);
- string dstr = to_string(dval);
- cout << istr << endl;
- cout << dstr << endl;
-
- istr = "9999";
- dstr = "88.88";
- ival = stoi(istr);
- dval = stod(dstr);
- }

-
相关阅读:
deepstream6.2部署yolov5详细教程与代码解读
闭包的详细认识与实例
入门力扣自学笔记149 C++ (题目编号1608)
HelloGitHub 社区动态,开启新的篇章!
大一新生常踩的5大坑,轻则损失钱财,重则影响毕业
模块及分类
阿拉伯数字转汉子数字
docker安装部署nginx
最大似然函数 损失函数 逻辑回归与线性回归的比较
驱动开发:通过MDL映射实现多次通信
-
原文地址:https://blog.csdn.net/weixin_63543274/article/details/126288655