1.string尾插
在一个string类型的字符后面插入其它的字符可以用push_back和append,这俩个是常用的。
#include<iostream>
using namespace std;
int main()
{string s1("hello");s1.push_back(' ');s1.push_back('w');s1.push_back('o');s1.push_back('r');s1.push_back('l');s1.push_back('d');cout << s1;return 0;
}
使用push_back需要注意的是参数是char型的,所以要用‘’,且里面是单个字符,不能是字符串。
#include<iostream>
using namespace std;
int main()
{string s1("hello");s1.append(" world1");cout << s1;return 0;
}
使用append函数时参数就是字符串用”“符号。
2.在string中插入数据
往string s1中插入数据可以用insert,可以头插尾插或者是原数据的任意位置。
实现头插
#include<iostream>
using namespace std;
int main()
{string s1("hello");s1.append(" world1");s1.insert(0, "ni hao");cout << s1;return 0;
}
指定一个位置插入数据,在索引为0和6的位置插入,索引6就是在w的前面。
#include<iostream>
using namespace std;
int main()
{string s1("hello");s1.append(" world1");s1.insert(0, "ni hao");s1.insert(6, " hh ");cout << s1;return 0;
}
需要注意在索引5插入&&,insert这个函数是不会覆盖原内容的,会把它挤到后面,这里插入俩个&,所以s1的大小是变大的。
#include<iostream>
using namespace std;
int main()
{string s1("hello");s1.append("world1");s1.insert(5, "&&");cout << s1;return 0;
}
用迭代器来,begin()是起始的位置,插入一个‘a’。
#include<iostream>
#include<string>
#include<map>
using namespace std;int main()
{string s1("hello");//char ch = 'a';s1.insert(s1.begin(), 'a');//写成ch也可以cout << s1;return 0;
}
3.string的删除操作
int main()
{string s1("hello world");s1.erase(6, 2);cout << s1;return 0;
}
使用erase函数去删除指定元素,这里是删除索引为6并且从此位置开始先后俩个元素都被删除,就是第一个参数是指定位置,第二个参数是删除多少个数据。
实现头删
#include<iostream>
#include<string>
#include<map>
using namespace std;int main()
{string s1("hello world");s1.erase(0,1);cout << s1;return 0;
}
把索引为0位置的元素删除。
也可以使用迭代器实现头删
int main()
{string s1("hello world");s1.erase(s1.begin());cout << s1;return 0;
}
注意:不可以在写参数1,因为erase函数有多个重载函数,如果要写了begin()还要在后面再写1的话会报错的,因为其中一个的函数重载是要俩个参数类型都是迭代器的。
iterator erase(iterator position);
iterator erase(iterator first, iterator last);
删除最后一个数据
int main()
{string s1("hello world");s1.erase(s1.size()-1,1);cout << s1;return 0;
}
这里size-1是因为左闭右开,size是最后一个元素的下一个位置。
实现pos位置的查找
int main()
{string s1("hello world hh hh");size_t pos = s1.find(' ');cout << pos;return 0;
}
这里会去找第一次出现空格的位置,后面再出现则不会返回索引。
2.替代指定位置数据replace
替代指定位置元素常用有replace
int main()
{string s1("hello w orld hh hh");size_t pos = s1.find(' ');while (pos != string::npos){s1.replace(pos, 1, "&&");cout << s1 << endl;pos = s1.find(' ', pos + 2);}return 0;
}
这里先去找空格的索引,然后替换为&&,后面再更新pos,pos+2是因为插入的&&是俩个字符,所以要在插入的后面重新再去找空格,1是pos位置的一个字符,插入俩个字符&&是会扩大字符长度。
另外一种实现方式
int main()
{string s1("hello w orld hh hh");size_t pos = s1.find(' ');string s2;for (auto ch : s1){if (ch == ' '){s2 += "&&";}else{s2 += ch;}}s1.swap(s2);cout << s1;return 0;
}
建立另一个string s2,用范围for去逐个判断,为空格就要重载+=”&&“,这里+=实际就是尾插,不为‘ ’就尾插原元素,这样就把空格处都变为&&,然后再swap去交换俩个内容。
swap
的作用
swap
是一个成员函数,用于交换两个字符串的内容而不需要进行实际的数据拷贝。这是一个高效的操作,因为它通常只涉及交换指针和大小等元数据,而不是逐个字符的复制。- 在这段代码中,
s1.swap(s2)
使得s1
最终保存了新的替换后的字符串,s2
则变成了一个空字符串。这种方法在处理字符串时,既简单又高效。
3.读取当前文件内容
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include<map>using namespace std;int main()
{string s1("hello w orld hh hh");size_t pos = s1.find(' ');string s2;for (auto ch : s1){if (ch == ' '){s2 += "&&";}else{s2 += ch;}}s1.swap(s2);cout << s1;string file;cin >> file;FILE* fout = fopen(file.c_str(), "r");char ch = fgetc(fout);while (ch != EOF){cout << ch;ch = fgetc(fout);}fclose(fout);return 0;
}
使用C语言的fopen去打开文件,.c_str()是把类型为string的file变为指针,”r“ 书读操作,
fgetc是读取一个字符的函数,循环的判断是当读取为EOF(文件或者数据流的末尾)停下,注意每次调用fgetc时都会自动下一个而不是一直读取一开始的哪一个,打开文件还需要关闭文件。
4.rfind和substr函数
int main()
{string s("abc.d.wwwwhhhh");size_t pos = s.rfind('.');string suffin = s.substr(pos);cout << suffin;return 0;
}
这rfind就是从后面开始寻找‘.’,而substr是子串,参数pos是从pos的位置开始(包括pos) 往后的数据拷贝到suffin。
std::string substr(size_t pos = 0, size_t len = npos) const;
5.find_first_of函数
int main()
{string s("abc.d.wwwwhhhh");size_t found = s.find_first_of("wd");while (found != std::string::npos){s[found] = '*';found = s.find_first_of("wd", found + 1);}cout << s;return 0;
}
find_first_of会把()里面的内容的索引返回,这里while就是替换完才停下,循环结束时w和d都被替换为*,found+1就是更新寻找位置,避免重复找同一个地方。
std::string
类提供了一个非常有用的成员函数find_first_of
,用于查找字符串中首次出现某个字符或字符集合的位置
6.string的getline
当我们想要输入时如果有空格,查看时是没有空格的,因为空格是停止符号,想要保留空格就需要使用getline,getline默认是遇到换行才停止,也可以改成其它的作为停止符号。
在C++中,当你使用标准输入流(
std::cin
)来读取数据时,默认的行为是以空格、换行符和制表符为分隔符。这意味着,当你尝试读取一个字符串或者其他类型的数据时,输入流会在遇到第一个空格、换行符或制表符时停止读取。
int main()
{string a;getline(cin, a);return 0;
}
这样就可以把空格给输入进去了,下面是把终止符改为*,遇到*就会停下。
(没有内容” “,当常量字符串规定会有/0在后面)
易错处
int main()
{string s1("hello");string s2 = s1 + "world";cout << s2 << endl;string s3 = "world" + s1;cout << s3 << endl;return 0;
}
这s2的操作是合理的,而s3按理会报错,但是没有是因为隐式转换为string,因为不是成员函数是不可以实现字符+类的,因为成员函数都有隐式参数*this,所以如果字符在第一个就类型不符合了。所以全局的函数可以。
7.模拟实现string的一些操作
关于c_str
string():_str(0),_size(0),_capacity(0){}
namespace zym
{void test(){string s1;cout << s1.c_str();}}
int main()
{zym::test();return 0;
}
在构造函数时给——str初始化为0导致程序崩溃,所以要给_str初始化为\0,给一个字符。
string():_str(new char[1]{'\0'}),_size(0),_capacity(0){}
在 C++ 中,c_str() 是 std::string 类的一个成员函数,用于返回一个 C 风格的字符串(const char*)。这个函数会返回一个指向字符串内部字符数组的指针,该指针以空字符('\0')终止。
为什么 c_str() 在对象为空指针时会崩溃
std::string 对象的 c_str() 方法是设计为在该对象有效且存在时返回一个有效的指针。如果你试图对一个已经被销毁或未初始化的 std::string 对象调用 c_str(),这会导致程序崩溃。以下是一些可能导致崩溃的场景:1.使用空指针(未初始化的 std::string 对象):
如果你有一个 std::string 对象的指针,但这个指针未被初始化或已被设置为 nullptr,调用 c_str() 会试图解引用空指针,这会导致未定义行为(通常是崩溃)。std::string* strPtr = nullptr;
const char* cStr = strPtr->c_str(); // 崩溃,因为 strPtr 是一个空指针
2.访问已经被销毁的 std::string 对象:
如果一个 std::string 对象已经被销毁(比如超出作用域),然后你试图访问它的 c_str(),这也会导致未定义行为。const char* cStr;
{
std::string str = "Hello, world!";
cStr = str.c_str(); // 在 str 的作用域内有效
}
// str 超出作用域并被销毁,此时 cStr 指向一个已销毁的内存
3.对象处于非法状态:
如果 std::string 对象的内部状态因某种原因(如编程错误或内存破坏)变得非法,尝试访问 c_str() 也可能导致崩溃。如何避免这些问题
4.确保对象有效:
在调用 c_str() 前,确保 std::string 对象有效且处于合法状态。避免使用空指针或悬挂指针。std::string str = "Hello";
const char* cStr = str.c_str(); // 确保 str 是有效的 std::string 对象
5.避免使用空指针:
不要对空指针调用 c_str() 或其他成员函数。如果你有一个指针,先检查它是否为 nullptr。std::string* strPtr = new std::string("Hello");
if (strPtr) {
const char* cStr = strPtr->c_str(); // 确保 strPtr 不为空
}
delete strPtr;总结
c_str() 方法是设计为在有效的 std::string 对象上调用的。如果 std::string 对象本身是空指针,或者已被销毁,或者对象的状态不合法,调用 c_str() 会导致崩溃或未定义行为。因此,在使用 c_str() 时,确保对象的有效性和生命周期是避免崩溃的关键。
打印c_str时不是指针而是内容,是因为C++会对其做处理,要想看地址需要强置转换void*。
在 C++ 中,std::cout 对 C 风格字符串(即 const char* 类型)有特殊的处理。当你使用 std::cout 输出一个 const char* 指针时,它不会直接打印指针的地址,而是会输出指针所指向的字符串内容。下面是这个过程的详细解释:
1. 重载运算符:
std::cout 是 C++ 标准库中的输出流对象,它重载了 << 运算符,以便可以处理多种数据类型。当你用 cout << 输出一个 const char* 类型的指针时,实际上调用了以下重载版本的 << 运算符:
std::ostream& operator<<(std::ostream& os, const char* s);这个重载版本的实现是专门用于处理 C 风格字符串的,它会逐字符输出字符串,直到遇到 null 字符('\0'),这使得输出的是字符串的内容,而不是指针的值。
2. 输出指针 vs 输出内容:1.打印指针的地址:
如果你直接尝试输出一个指针的地址,例如 std::cout << (void*)pointer;,那么输出的将是该指针所指向的内存地址。
2.打印指针所指向的内容:
使用 std::cout << pointer;,则会调用处理 C 风格字符串的重载版本,输出的是字符串内容,而不是地址。3. 示例代码:
以下是一个示例代码,展示了打印指针地址和打印字符串内容的区别:
#include <iostream>
#include <string>int main() {
std::string str = "Hello, World!";
const char* cStr = str.c_str();// 输出字符串内容
std::cout << "内容: " << cStr << std::endl; // 输出: Hello, World!// 输出指针的地址
std::cout << "指针地址: " << (void*)cStr << std::endl; // 输出: 指针的内存地址return 0;
}4. 输出结果:
运行上述代码时,输出将类似于:
内容: Hello, World!
指针地址: 0x7ffee6b7c3d0 // 这个值会因每次运行而不同
3.第一个输出显示了字符串的内容。
4.第二个输出显示了指针的内存地址。总结:
5.当使用 std::cout << 输出 const char* 时,C++ 使用了重载运算符来处理该指针,输出其指向的字符串内容。
6.如果希望输出指针的地址,则需要显式地转换指针类型,例如 (void*)pointer。
关于初始化
如果使用原括号则里面不应该有参数,且是把所有元素初始化为0,使用花括号则可以自定义初始化元素
在 C++ 中,圆括号 () 和花括号 {} 都可以用于初始化动态分配的内存,但它们的使用方式略有不同。具体来说,new char[1]() 和 new char[1]{'\0'} 这两种方式都可以用来创建和初始化一个包含一个字符的数组,但它们在初始化时的行为略有不同。
1. 使用圆括号 ()
当你使用圆括号 () 来初始化动态分配的数组时,编译器会将数组的所有元素初始化为默认值:
char* str = new char[1](); // 使用圆括号进行初始化在这个例子中:
1.new char[1]() 分配了一个大小为 1 的字符数组,并将其初始化为零。
2.对于 char 类型,零值就是 '\0'(null 字符),所以这与使用 {'\0'} 的效果相同。2. 使用花括号 {}
当你使用花括号 {} 来初始化动态分配的数组时,你可以明确指定数组元素的初始化值:
char* str = new char[1]{'\0'}; // 使用花括号进行初始化在这个例子中:
3.new char[1]{'\0'} 分配了一个大小为 1 的字符数组,并将其初始化为 '\0'(null 字符)。
3. 比较
4.圆括号 ():提供了一个简单的初始化方式,将所有元素初始化为零(或默认构造值)。对于内置类型如 char,零初始化就是 '\0'。
5.花括号 {}:允许更显式的初始化,可以初始化部分或全部元素,并且支持列表初始化。如果提供的初始值少于数组大小,未初始化的元素会被零初始化。4. 示例代码
下面是一个示例,演示了两者的效果:
#include <iostream>
#include <cstring>int main() {
char* str1 = new char[1](); // 使用圆括号初始化
char* str2 = new char[1]{'\0'}; // 使用花括号初始化std::cout << "str1 内容: '" << str1 << "'" << std::endl; // 输出: str1 内容: ''
std::cout << "str2 内容: '" << str2 << "'" << std::endl; // 输出: str2 内容: ''std::cout << "str1 字符串长度: " << strlen(str1) << std::endl; // 输出: str1 字符串长度: 0
std::cout << "str2 字符串长度: " << strlen(str2) << std::endl; // 输出: str2 字符串长度: 0delete[] str1; // 释放内存
delete[] str2; // 释放内存return 0;
}总结
6.圆括号 () 和 花括号 {} 都可以用来初始化动态分配的数组。
7.new char[1]() 和 new char[1]{'\0'} 实际上会产生相同的效果,对于 char 类型,数组中的唯一元素会被初始化为 '\0'。
8.使用圆括号 () 是一个简洁的方式来进行零初始化,而使用花括号 {} 则可以提供更详细的初始化列表。
关于迭代器
迭代器就像支付宝一样,以前网上用钱需要操作比较麻烦,每个银行的操作都不一样,密盾,扫脸什么的,现在可以直接扫码,因为那些银行的操作在支付宝那里弄好了,迭代器把不同接口的操作都处理了,我们只需要按照统一的方式就可以访问。
在 C++ 中,迭代器是一个用于遍历容器(如 vector、list、map 等)元素的对象。它提供了一种统一的方式来访问容器中的数据,而不需要直接处理底层数据结构的细节。迭代器可以被视为指向容器中元素的指针,但提供了更丰富的功能。
迭代器的类型
C++ 中的迭代器根据其功能和特性可以分为几种类型:1.输入迭代器(Input Iterator):
2.只允许读取数据,不能修改。
3.可以进行递增操作。
4.输出迭代器(Output Iterator):
5.只允许写入数据。
6.可以进行递增操作。
7.前向迭代器(Forward Iterator):
8.允许读取和写入数据。
9.可以多次访问相同的元素。
10.只能向前移动。
11.双向迭代器(Bidirectional Iterator):
12.允许读取和写入数据。
13.可以向前和向后移动。
14.随机访问迭代器(Random Access Iterator):
15.支持直接访问容器的任意元素。
16.允许进行加法和减法运算。迭代器的基本用法
下面是一些使用 C++ STL(标准模板库)迭代器的示例:
1. 使用 vector 的迭代器
#include <iostream>
#include <vector>int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};// 使用迭代器遍历 vector
std::vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;return 0;
}在这个示例中,vec.begin() 返回指向 vector 第一个元素的迭代器,而 vec.end() 返回指向 vector 最后一个元素后面的迭代器。
2. 使用 list 的迭代器
#include <iostream>
#include <list>int main() {
std::list<std::string> myList = {"apple", "banana", "cherry"};// 使用迭代器遍历 list
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " "; // 输出: apple banana cherry
}
std::cout << std::endl;return 0;
}这里使用 auto 关键字简化迭代器的声明。
3. 使用 map 的迭代器
#include <iostream>
#include <map>int main() {
std::map<std::string, int> myMap = {{"apple", 1}, {"banana", 2}, {"cherry", 3}};// 使用迭代器遍历 map
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl; // 输出: apple: 1, banana: 2, cherry: 3
}return 0;
}在 map 中,迭代器的 first 成员表示键,second 成员表示值。
迭代器的优点17.抽象化:迭代器提供了一种统一的接口来访问不同类型的容器。
18.简化代码:可以使用相同的遍历方式处理不同类型的容器。
19.提高可读性:使用迭代器可以使代码更易读、易理解。其他注意事项
20.迭代器的有效性:当容器被修改(如插入或删除元素)时,某些迭代器可能会失效,尤其是 vector 和 deque。应谨慎操作。
21.迭代器的常量性:可以使用常量迭代器(如 std::vector<int>::const_iterator)来防止修改容器中的元素。总结
C++ 的迭代器是一种强大且灵活的工具,允许我们以一种一致的方式访问和操作不同类型的容器。通过理解和使用迭代器,可以有效提高代码的可读性和可维护性。如果你还有其他问题或需要更多示例,欢迎继续提问!
- 避免重复定义:在头文件中仅提供声明,多个源文件可以包含同一个头文件,而不会导致函数或类的重复定义错误。
原生指针模拟迭代器
string s("hello");
for (auto ch : s)
{cout << ch << endl;
}
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}
因为迭代器跟指针相似,所以用原生指针去模拟迭代器,手写一个begin()和end()函数去满足范围for的需求,能用原生指针模拟迭代器是因为string的_str是数组,别的可能模拟不了,底层是数组才可以用原生指针模拟迭代器,迭代器屏蔽了底层细节,因为不论底层是什么begin()就是头,end()就是尾,迭代器是封装的体现,另外如果把begin()变成Begin()就不行,因为这个是傻瓜式的替换,必须跟正版的名字一样。
8.代码讲解
模拟实现迭代器的功能,通过返回地址的方法来实现,_size+_str是最后一个元素的地址,这了const表示的是另一种类型迭代器,表示所有内容都不可修改。
typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}
这里赋值”“是因为常量字符串 规定后面自带一个/0,所以是初始化为0,capacity+1是因为要给/0留位置,strcpy函数可以把str的内容拷贝到_str上面去。
string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}
返回string对象的地址。
const char* c_str() const{return _str;}
清除内容,这里把第一个字符设置为0,实际上是用来标识字符串的结束,当字符串第一个字符为‘\0’时,表示这个字符串是空的,设置一个字符的原因是效率块,不需要去遍历数组去清空,
void clear(){_str[0] = '\0';_size = 0;}
尾插首先要判断其空间与大小想不想等,去做出扩容操作,这里用三目操作符来做判断,为0就开四个大小的空间,不为0就开原来的2倍空间大小,注意尾插完后还需要补会一个终止符号‘\0’,不然就会出现问题,乱码的问题。
void string::push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '/0';//因为/0被覆盖了所以需要重新加上/0}
这里是直接给空间开确定大小,当要给'\0 '留一个位置,然后把原内容移动过去,删除原来空间,指向新开辟更大的空间。
void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//给/0留一个位置strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
这也是尾插的作用,先得到要加入的字符长度len,还要判断加入后是否超出空间大小,_str+_size是原数据最后的位置,所以把新来到数据接到最后面去。
void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, str);_size += len;}
这里的end=_size+1是因为要把'\0'算进去,因为插入要把后面的进行挪动,通过控制end去把袁术进行后移,把pos的位置看出来,插入元素后还需要让_size+1。
void 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];--end;}_str[pos] = ch;++_size;}
9.代码展示
string.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once#include<iostream>
#include<string>
#include<assert.h>
using namespace std;namespace zym
{class string{public:typedef char* iterator;typedef const char* const_iterator;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);}~string(){if (_str){delete[] _str;_str = nullptr;_size = _capacity = 0;}}const char* c_str() const{return _str;}void clear(){_str[0] = '\0';_size = 0;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}void reserve(size_t n);void push_back(char ch);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, size_t len = npos);void append(const char* str);//void test();private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;//static const size_t npos=1;//这里是定义的行为,static const才可以,其它不写而且还要是 整型//给static const整型开绿灯 为了方便下面行为//static const int N =10//int buff[N] static const size_t npos;};
}
string.cpp
#include"string.h"namespace zym
{const size_t string::npos = -1;void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//给/0留一个位置strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '/0';//因为/0被覆盖了所以需要重新加上/0}string& string::operator+=(char ch){push_back(ch);return *this;}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, str);_size += len;}string& string::operator+=(const char* str){append(str);return *this;}void 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];--end;}_str[pos] = ch;++_size;}void string::insert(size_t pos, const char* s){assert(pos <= _size);size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}for (size_t i = 0; i < len;){_str[pos + i] = s[i];}_size += len;}}
test.c
#include"string.h"
//using namespace std;namespace zym
{void test(){string s1("hello");//cout << s1.c_str();s1 += "x";cout << s1.c_str();//cout << s1;//for (auto ch : s1)//{// cout << ch << endl;//}}}
int main()
{zym::test();return 0;
}