当前位置: 首页 > news >正文

如何查看网站是否被做跳转哪家企业的网站做的好

如何查看网站是否被做跳转,哪家企业的网站做的好,什么是产品网络推广,做seo网站诊断书怎么做在上一篇文章中我们介绍了string类的基本使用#xff0c;本篇文章我们将讲解string类一些常用的模拟实现#xff0c;其中有很多细小的知识点值得我们深入学习。Let’s go#xff01; 文章目录 类声明默认成员函数构造函数析构函数拷贝构造函数深浅拷贝问题传统写法现代写法… 在上一篇文章中我们介绍了string类的基本使用本篇文章我们将讲解string类一些常用的模拟实现其中有很多细小的知识点值得我们深入学习。Let’s go 文章目录 类声明默认成员函数构造函数析构函数拷贝构造函数深浅拷贝问题传统写法现代写法 赋值运算符重载传统写法现代写法 容器操作获取长度size获取当前容量capacity查询是否为空empty扩容reserve调整字符串大小resize 字符串访问[]访问迭代器访问 插入类尾插一个字符push_backappend在尾部追加一个string对象在尾部追加一个C风格字符串在尾部追加n个字符 insert在pos位置插入一个字符在pos位置插入一个字符串 operator 删除类erase 其他操作swapfind返回字符c在string中第一次出现的位置返回子串s在string中第一次出现的位置 substrprintf_strclearc_str逻辑判断 流操作流插入流提取 拓展:关于string其他常用函数to_stringstoi 完整代码展示.h文件.cpp文件 类声明 namespace Mystring {// 定义一个字符串类class string{public:// 公共成员函数和接口private:// 私有成员变量限制直接访问底层数据size_t _capacity 0; // 字符串的容量size_t _size 0; // 字符串的长度char* _str nullptr; // 指向字符串数据的指针const static size_t npos -1; // 静态常量表示未找到位置或无效位置}; } 在C中静态成员变量static的定义通常需要在类的外部进行而非静态成员变量则需要在类的内部进行定义。然而对于静态成员变量如果其为 const 且为整数类型包括枚举类型则可以在类内部直接进行初始化。因此对于 const static size_t npos -1; 这样的声明其允许在类内部直接进行初始化。对于npos的初始化下面两种方式都可以 class string { public:static const size_t npos -1; }; class string { public:static const size_t npos; };const size_t string::npos -1; 声明变量时可以顺便初始化这样可以确保对象在创建时具有合适的初始值。结构上使用了命名空间Mystring来避免与标准库中的 std::string 冲突。本篇文章的代码采用声明与定义分离的方式。声明放在string.h文件定义放在string.cpp文件。.cpp文件中通过包对应头文件以及声明命名空间然后通过类名::成员的方式定义和实现函数。 默认成员函数 构造函数 声明 string(const char*str); //(提供了一个缺省值表示在没有提供参数时str 默认初始化为一个空字符串 //即一个以 null 结尾的字符数组其中只有一个字符 \0)这里是 “不是” 。后者不为空 有一个空格. 定义 string(const char* str ) {_size strlen(str); // 计算字符串长度_capacity _size; // 初始容量与字符串长度相同_str new char[_capacity 1];// 为字符串分配内存空间多开一个空间用于存放 \0strcpy(_str, str); // 将参数 str 的内容拷贝到 _str 中 }如 string::string s1(Hello); 析构函数 ~string() {delete[] _str;// 释放字符串的内存空间使用 delete[] 因为 _str 是数组形式的字符串_size 0; // 将字符串长度置为 0表示字符串已经被释放_capacity 0;// 将容量置为 0表示容量无效_str nullptr; // 将指向字符串数据的指针置为 nullptr防止出现悬空指针 }析构函数在对象被销毁时自动调用通常用来释放对象所持有的资源例如动态分配的内存。 **拓展**悬空指针 是指指向已经被释放或者无效的内存地址的指针。当一个指针被赋予了 nullptr 或者指向的内存已经被释放时这个指针就变成了悬空指针。 在C中如果一个对象的析构函数中没有将指针设置为 nullptr那么当对象被销毁时其指针成员可能会成为悬空指针。悬空指针引发的问题主要有两个 未定义行为Undefined Behavior如果试图通过悬空指针访问内存则会导致未定义行为这可能会导致程序崩溃或者产生难以预料的结果。内存泄漏或重复释放悬空指针可能会导致内存泄漏因为释放过的内存没有被正确释放或者在程序的其他地方被重新分配导致对同一块内存的多次释放。 在编程中为了避免悬空指针的问题通常有以下建议 析构函数中将指针置为 nullptr在对象被销毁时确保将指针成员设置为nullptr这样可以避免在对象的生命周期结束后访问悬空指针。使用智能指针C11引入的智能指针如std::unique_ptr和std::shared_ptr可以帮助自动管理动态内存避免手动释放内存和悬空指针问题。注意指针的生命周期确保在指针可能成为悬空指针的情况下适时将其置为nullptr或者避免在对象生命周期结束后继续使用该指针。 通过良好的编程实践和注意内存管理可以有效避免悬空指针带来的问题提高程序的健壮性和可靠性。 拷贝构造函数 深浅拷贝问题 如果我们不写拷贝构造函数编译器会默认生成一个浅拷贝的拷贝构造函数。但是默认生成的拷贝构造函数只会简单地逐成员进行赋值拷贝这在处理指针成员变量时会导致严重问题 当使用默认的浅拷贝构造函数时两个对象会共享同一个内存空间,会导致以下问题 共享内存s1 和 s2 共享同一块内存这意味着修改一个对象会影响另一个对象。悬空指针当 s1 或 s2 析构时内存会被释放另一个对象的指针会变成悬空指针。双重释放当 s1 和 s2 都析构时会尝试释放同一块内存两次导致程序崩溃。 为了解决浅拷贝带来的问题我们需要实现一个深拷贝的拷贝构造函数。深拷贝会为新对象分配独立的内存空间并将原对象的数据复制到新对象中从而避免共享内存的问题。 传统写法 string(const string s) {// 为新对象分配独立的内存空间并且多分配一个字节用于存储终止符 \0_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 交换当前对象和临时对象的 _str 指针。std::swap(_size, s._size);//使用 std::swap 交换当前对象和临时对象的 _size 值。std::swap(_capacity, s._capacity);//使用 std::swap 交换当前对象和临时对象的 _capacity 值}//s2(s1) //下面的s 就是s1string(const string s) :_str(nullptr),_size(0),_capacity(0){string tmp(s._str); // 注意是构造 // 使用 s 对象的内部 C 风格字符串 _str 构造一个临时的字符串对象 tmpswap(tmp); // 交换当前对象和临时对象的数据使当前对象的内容变为 tmp 的内容临时对象则被销毁} //解析tmp和s1有一样大的空间一样的值。然后s2和tmp一交换,那s2就和s1一样了就完成了。 图解 交换前交换后在C中当我们用string s2(s1)来创建string对象时s1是用来初始化string s2的源对象。现代写法中的string(const string s)构造函数会被调用来实现这一点。理解现代写法在这个构造函数中我们可以理解成 调用构造函数 当我们写string s2(s1)时编译器调用string类的拷贝构造函数string(const string s)。这里的s就是s1表示用s1对象来初始化新创建的s2对象。 创建临时对象 在构造函数内部首先使用s对象即s1的内部 C 风格字符串_str来构造一个临时对象tmp。string tmp(s._str)这行代码会调用另一个构造函数string(const char* str)用s1对象的字符串数据来初始化临时对象tmp。 交换数据 调用swap(tmp)将当前对象即s2的成员变量与临时对象tmp的成员变量进行交换。在交换之后s2对象持有了tmp的数据即持有了s1的数据副本而tmp则持有了s2的初始数据在这时通常为空或者默认值。 析构临时对象 当构造函数结束时临时对象tmp离开作用域自动析构释放它持有的资源。由于tmp持有的是s2的初始数据在构造时通常是无效数据所以释放时不会影响s2也不会造成资源泄漏。 这种现代写法通过创建临时对象和交换数据确保了拷贝构造的简洁性和异常安全性同时避免了资源泄漏和浅拷贝带来的问题。 ❓为什么要在初始化列表中给 _str 初始化为空指针 string(const string s) : _str(nullptr) 如果不对它进行处理一开始指向的是 未定义的随机值)。在交换之后这个随机值就给了tmp了tmp出了作用域后调用析构函数进行释放会对随机值指向的空间进行释放。 这种情况下系统可能无法正确处理释放操作从而导致程序崩溃或者其他未定义行为。 delete 或者 free 一个空指针是安全的操作不会导致运行时错误所以这里把它初始化为nullptr,tmp最后释放空不会出现问题。 赋值运算符重载 传统写法 string operator(const string s) {if (this ! s) // 防止自我赋值{char* tmp new char[s._capacity 1]; // 为临时存储空间分配内存大小为 s 对象的容量加一用于存放字符串末尾的 \0strcpy(tmp, s._str); // 将 s 对象的字符串复制到临时存储空间 tmpdelete[] _str; // 删除当前对象已有的字符串内存_str tmp; // 将当前对象的 _str 指向新分配的字符串内存_size s._size; // 更新当前对象的字符串长度_capacity s._capacity; // 更新当前对象的容量}return *this; // 返回当前对象的引用支持连续赋值操作 }传统写法图解 现代写法 //s1s3 string operator(string s) // 使用传值方式传入参数 s利用了移动语义 {swap(s); // 使用交换函数进行赋值操作此时 s 是通过拷贝构造函数传入的临时对象return *this; // 返回当前对象的引用支持连续赋值操作 }交换前交换后在 string operator(string s) 中使用传值传参主要有以下几个原因 移动语义的利用 传值传参允许编译器在需要的时候使用移动语义这样可以避免不必要的深拷贝提升性能。如果传递的参数是右值例如s1 std::move(s3)则会调用移动构造函数而不是拷贝构造函数从而避免了数据的复制。 简化代码 通过传值传参可以在函数体内直接交换当前对象和参数对象的数据。这使得代码更简洁并且更容易理解和维护。 异常安全性 传值传参结合交换操作可以确保资源的正确释放避免资源泄漏和其他异常问题。 详细过程解释假设我们有以下赋值操作s1 s3; 传值传参 string s(s3); // 临时对象 s 通过拷贝构造或移动构造函数创建当调用s1 s3;时会创建一个临时对象s。这个临时对象s是通过拷贝构造函数如果s3是左值或移动构造函数如果s3是右值创建的。 交换操作 swap(s); // 交换 s1 和 s 的数据在赋值运算符的实现中调用swap(s);。这会交换当前对象s1和临时对象s的内部数据指针。 临时对象销毁 // 临时对象 s 离开作用域被销毁释放旧资源在赋值运算符函数结束时临时对象s离开作用域并被销毁其析构函数会释放它所持有的资源。这些资源实际上是原来属于s1的旧资源。 返回当前对象 return *this; // 返回当前对象的引用返回当前对象s1的引用以支持连续赋值操作。 容器操作 获取长度size size_t size() const //考虑到不需要修改我们加上 const。{return _size;}获取当前容量capacity size_t capacity() const{return _capacity;}查询是否为空empty bool empty() const{return _size 0;}扩容reserve void reserve(size_t n) {// 如果请求的容量大于当前的容量才需要重新分配内存if (n _capacity){char* tmp new char[n 1]; // 分配新的内存空间比请求的容量多一个字符// 这个额外的字符用于存放字符串结尾的空字符 \0确保字符串的有效性和正确性strcpy(tmp, _str); // 将原字符串内容拷贝到新内存也会拷贝结尾的 \0delete[] _str; // 释放原来的内存_str tmp; // 更新指针使其指向新的内存_capacity n; // 更新容量} } 扩容扩容所以n要≥_capacity 调整字符串大小resize 记得用缺省值,用户在调用 resize 函数时可以选择性地提供第二个参数.假如有一个字符串对象 str当前大小为 5内容为 “hello”容量为 10。调用 str.resize(8, ‘x’) 后 就是 helloxxx\0声明void resize(size_t, char c \0);定义 void resize(size_t n, char c) {// 如果新的大小大于当前大小需要扩展字符串if (n _size){// 如果新的大小大于当前容量需要扩展内存if (n _capacity){reserve(n); // 调用 reserve 函数扩展容量}// 将新的字符填充到扩展后的字符串中for (size_t i _size; i n; i){_str[i] c;}}else if (n _size){// 如果新的大小小于当前大小只需更新大小_size n; // 注意此时容量不会改变}// 更新字符串的实际大小并确保字符串以空字符结尾_str[_size] \0; } 缩容就直接在下标为n的位置设置为\0即可。 字符串访问 []访问 //仅能访问const char operator[](size_t pos) const{assert(pos _size);//assert 括号里为假的时候才会报错return _str[pos];}//访问修改char operator[](size_t pos){assert(pos _size);return _str[pos];}迭代器访问 迭代器在 C 中常常被描述为类似指针的对象它提供了对容器比如字符串中元素的访问和操作。对于模拟实现的字符串类我们可以直接使用原生指针来作为迭代器通过 typedef 进行重命名这样就可以在类中直接使用迭代器。首先我们使用 typedef 将指针重命名为迭代器同时定义了常量迭代器typedef char* iterator;typedef const char* const_iterator; // 返回字符串的起始位置iterator begin() {return _str;}// 返回字符串的结束位置\0 的下一个位置即 null 字符的位置iterator end() {return _str _size;}// 返回字符串的起始位置const 版本不能修改数据 //常量成员函数 //const_iterator begin() const 和 const_iterator end() const 被声明为常量成员函数。 //这意味着它们不会修改对象的任何成员变量并且它们可以被常量对象调用。//为什么需要最右边的const??? //如果没有最后的 const 修饰符编译器将认为 begin() 和 end() 可能会修改对象。因此当你试图在一个常量对象上调用这些函数时会产生编译错误因为编译器不允许通过常量对象调用非常量成员函数。const_iterator begin() const {return _str;}// 返回字符串的结束位置的下一个位置const 版本const_iterator end() const {return _str _size;}这些函数使得我们可以像操作指针一样操作迭代器比如使用 和 – 来移动迭代器指向的位置或者使用 * 来访问迭代器指向的元素。这样我们就可以通过迭代器来遍历字符串中的字符了。 插入类 尾插一个字符push_back void push_back(char ch){if (_size _capacity)//大小和总容量一样的时候说明不够用了{size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] ch;_size;_str[_size] \0;//一定要处理好\0}append 在尾部追加一个string对象 string append(const string str) {// 检查是否需要扩展容量if (str._size _capacity - _size) // 判断是否需要扩容{reserve(_capacity str._size 1); // 扩容并预留足够空间}// 复制传入的字符串到当前字符串的末尾strcpy(_str _size, str._str); // _str _size 是当前字符串尾部// 更新当前字符串的大小_size str._size; // 更新_size// 手动设置字符串的结尾_str[_size] \0; // 手动设置字符串尾部的\0// 返回当前对象的引用return *this; // 返回string对象 } 在尾部追加一个C风格字符串 void append(const char* str) //注意传的是指针{ size_t len strlen(str);if (_size len _capacity){reserve(_size len);}//char *strcpy(char *dest, const char *src); strcat(_str,str)也行但是效率不行strcpy(_str _size, str);_size len;}在尾部追加n个字符 void append(size_t n, char ch){// 检查是否需要扩展容量if (_size n _capacity){reserve(_size n); // 扩展容量以容纳新字符}// 将字符 ch 追加 n 次到字符串末尾for (size_t i 0; i n; i){_str[_size i] ch;}// 更新字符串的大小_size n;// 确保字符串以 \0 结尾_str[_size] \0;}注意_size和_capacity是不计算\0的 insert 在pos位置插入一个字符 在 C 中通常情况下字符串的位置索引 pos 是从 0 开始的即第一个字符的位置为 0第二个为 1依此类推。这种习惯是因为 C 中的数组和字符串的索引都是从 0 开始计数的。 _str 表示字符串的起始位置即第一个字符的地址。 _str 1 表示字符串中第二个字符的地址。 _str pos 表示字符串中第 pos 个位置的地址即要进行插入或其他操作的位置。 void insert(size_t pos, char ch) {// 确保插入位置在有效范围内assert(pos _size); // pos 等于 _size 时表示尾插// 检查是否需要扩展容量if (_size _capacity) // 大小和总容量一样的时候说明不够用了{size_t newCapacity _capacity 0 ? 4 : _capacity * 2; // 扩展容量最小扩展到 4reserve(newCapacity); // 调用 reserve 函数扩展容量}// 从后往前 移动数据以腾出插入位置int end _size;while (end (int)pos) // 循环直到 end 小于 pos{_str[end 1] _str[end]; // 将当前位置的数据向后移动一位--end; // end 减 1}// 在指定位置插入新字符_str[pos] ch; // 在 pos 位置插入字符 ch_size; // 更新大小_str[_size] \0; // 确保字符串以 \0 结尾 } 为什么 while (end (int)pos) 要强制转换成int类型呢 因为end 是一个 size_t 类型的变量这是一个无符号整数类型。pos 也是 size_t 类型。如果直接比较 end 和 pos即使 end 被减到负值由于 size_t 是无符号类型负值会被当成一个非常大的正整数。这可能会导致无限循环和访问越界。 通过将 pos 强制转换为 int确保 end 和 pos 在比较时都是有符号整数类型从而避免了无符号整数类型转换的问题。这种做法保证了在 end 小于 pos 时循环能正确退出。 在pos位置插入一个字符串 void insert(size_t pos, const char* str) {// 确保插入位置在当前字符串长度范围内assert(pos _size);// 计算要插入字符串的长度size_t len strlen(str);// 如果当前容量不足以容纳插入后的新字符串则增加容量if (_size len _capacity){reserve(_size len); // 调用 reserve 函数扩展容量}// 使用有符号整数类型的 end 变量以避免无符号整数类型带来的潜在问题int end _size;// 从字符串末尾向前移动字符以腾出插入位置while (end (int)pos){_str[end len] _str[end]; // 将当前位置的数据向后移动 len 位--end; // end 减 1}// 将新的字符串插入到指定位置strncpy(_str pos, str, len); // 使用 strncpy 复制字符串内容但不包括末尾的 \0// 更新字符串的大小_size len; // 新字符串的长度增加_str[_size] \0; // 确保字符串以 \0 结尾 } operator string operator(char ch)//一个字符{push_back(ch);return *this;}string operator(const char* str)//一个 char* 字符串{append(str);return *this;}string operator (const string str) //一个string对象{append(str);return *this;}删除类 erase 从pos位置开始删除长度为len的字符串。若未给出len则默认删完.void erase(size_t pos, size_t len npos); void erase(size_t pos,size_t len) //pos 是下标删除1个就是pos位置的那个{//assert(pos _size);// xxxx size4,//assert(_size 0);assert(pos _size); // 这里不需要检查 pos 0因为 pos 是无符号类型if (len npos||poslen_size)//要删完{//但我们不用删直接缩大小_str[pos] \0;_size pos;}else {//后面数据挪过去覆盖// hello,wordl// ↑ ↑poslen// pos 删3个strcpy(_str pos, _str pos len);_size - len;//覆盖之后减少_size即可}//abcdefghi。假如pos是3,len是4。pos是下标//_str 指向字符串的第一个字符即 a。//_str pos 指向字符串的第 4 个字符即 d。//_str pos len 指向字符串的第 8 个字符即 h。}其他操作 swap 尽管标准库中的 std::swap 可以用于交换两个对象但是它仅在你提供的交换操作对你特定类的成员变量的交换上不能直接进行。标准库的 std::swap 无法直接处理类的私有成员变量的交换而必须通过类提供的接口进行交换操作。所以 自定义类型要自己写,上面的拷贝构造和赋值重载的现代写法都用到了此处的swap函数 void swap(string s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}find 返回字符c在string中第一次出现的位置 size_t find(char ch,size_t pos0) size_t find(char ch,size_t pos)//半缺省{for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;}返回子串s在string中第一次出现的位置 size_t find(const char* str,size_t pos0); size_t find(const char* str, size_t pos ) {// 使用 strstr 函数从 _str pos 位置开始查找子字符串 strconst char* ptr strstr(_str pos, str);// 如果 ptr 为空表示没有找到子字符串if (ptr nullptr){// 返回 npos 表示查找失败return npos;}else{// 返回子字符串在字符串中的起始位置return ptr - _str;} } strstr 是 C 语言标准库 或 string.h中的函数用于在一个字符串中查找第一次出现另一个字符串的位置。 constchar* strstr(constchar* str1, constchar* str2); str1要在其中搜索的主字符串。str2要搜索的子字符串。如果 str2 是 str1 的子串则返回指向 str1 中第一次出现 str2 的位置的指针。如果 str2 不是 str1 的子串则返回 nullptr。 substr 从当前字符串中提取子串。 string substr(size_t pos 0, size_t len npos); string substr(size_t pos , size_t len ){assert(pos _size);size_t end pos len;if (len npos || pos len _size){end _size;}string str;str.reserve(end - pos);for (size_t i pos; i end; i){str _str[i];}return str;//返回str的拷贝}printf_str 打印C风格字符串。C风格字符串是以空字符 ‘\0’ 结尾的字符数组 void printf_str(const string s)//权限放大上面的末尾要加const //在C中成员函数末尾的const关键字用于指示该函数不会修改对象的状态。这种函数被称为const成员函数它们对于保证对象的不可变性非常重要。 //c_str() 和 size() 都是访问器函数它们不会修改字符串对象的内容因此应该声明为const成员函数以确保它们可以在const对象上调用。 //这样做不仅符合面向对象的设计理念还允许用户在const对象上调用这些函数以便于在const上下文中使用你的类。 //而对于const char operator[](size_t pos) const它是一个重载的下标运算符用于访问字符串中指定位置的字符。由于该函数不会修改对象的内容因此也应该声明为const成员函数。{for (size_t i 0; i s.size(); i){// s[i]; 参数加const 就是为了防止这里进行修改。cout s[i] ;}cout endl;}clear 清空当前字符串对象使其变为空字符串 void clear(){_size 0;_str[0] \0;//}c_str 获取字符串源指针有些场景下例如使用C语言的字符串操作函数处理字符串时只能使用char*指针去传参string为了兼容C字符串操作函数支持获取字符串源指针为了不破坏string的数据结构这个返回的源字符串指针不支持修改只能访问内容这个函数非常短小直接在类中实现 const char* c_str() const {assert(_str);return _str;}逻辑判断 实现了小于和等于其他的直接复用. 都被声明为 const 成员函数。 const 关键字的作用是告诉编译器这些成员函数不会修改类的成员变量 _str 和 _size。 在 C 中类的 const 成员函数可以确保在函数内部不会修改对象的任何成员变量从而提供了对调用者的额外保证。这样的设计有助于代码的可维护性和可理解性。 如果没有将比较操作符声明为 const则无法在常量对象上调用这些操作符因为常量对象只能调用 const 成员函数。例如对于声明为 const 的对象或者在常量上下文中使用的对象如 const String s1, s2;可以正常地执行比较操作。 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); } 流操作 当我们在 C 中定义流插入运算符 和流提取运算符 时如果将它们定义为类的成员函数会遇到一个问题类的成员函数默认会有一个隐含的 this 指针作为第一个参数。这样的话如果我们试图将 operator 或 operator 定义为成员函数形式上会与预期不符因为它们需要接受两个参数左操作数和右操作数而类成员函数形式下只能接受一个参数除非将其定义为静态成员函数但这不符合重载运算符的惯用方式。因此为了正确地重载这些运算符我们将它们定义为类的友元函数。友元函数可以在不通过对象接口即不使用 this 指针的情况下访问类的私有成员和受保护成员。这种做法不仅符合语法要求还能保持类的封装性和安全性因为只有特定的函数即声明为友元的函数才能直接访问类的私有部分。 流插入 ostream operator(ostream out, const string s){for (auto ch : s){out ch;}return out;//返回ostream对象 以支持couts1s2s3}流提取 istream operator (istream in, string s){s.clear(); // 清空当前字符串以免变成尾插了char buff[128] {0}; // 创建一个缓冲区用于暂存读取的字符序列char ch in.get(); // 从输入流中读取一个字符int i 0; // 初始化缓冲区索引// 循环读取字符直到遇到空格或换行符while (ch ! ch ! \n){buff[i] ch; // 将读取的字符存储到缓冲区中if (i 127) // 如果缓冲区即将满了{buff[i] \0; // 在缓冲区末尾添加字符串终止符s buff; // 将缓冲区中的字符序列插入到字符串对象中i 0; // 重置缓冲区索引}ch in.get(); // 读取下一个字符}// 处理剩余的字符序列if (i 0){buff[i] \0; // 在缓冲区末尾添加字符串终止符s buff; // 将缓冲区中的字符序列插入到字符串对象中}return in; // 返回输入流对象的引用}拓展:关于string其他常用函数 to_string to_string 是 C 中的一个标准库函数用于将各种类型的数据转换为对应的字符串表示形式。 头文件:#includestring语法:std::string to_string(类型 value); 类型可以是整数、浮点数。value: 要转换为字符串的数值。 返回转换后的 std::string 类型对象表示数值的字符串形式。 stoi stoi 是 C 中的一个标准库函数用于将字符串转换为对应的整数类型。 头文件:#includestringint stoi(const std::string str, size_t* pos 0, int base 10); str: 要转换的字符串。pos (可选): 指向 size_t 类型的指针用于存储第一个无效字符的索引。base (可选): 数字的基数默认为 10。 返回 返回转换后的整数值。 完整代码展示 .h文件 #pragma once #define _CRT_SECURE_NO_WARNINGS 1 #includeassert.h #includeiostream using namespace std; namespace Mystring {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;}size_t size() const{return _size;}size_t capacity(){return _capacity;}bool empty() const{return _size 0;}void printf_str(const string s){for (size_t i 0; i s.size(); i){cout s[i] ;}cout endl;}void clear(){_size 0;_str[0] \0;//}const char* c_str() const{assert(_str);return _str;}string(const char* str );~string();string(const string s);string operator(string s);void reserve(size_t n);void resize(size_t, char c \0);const char operator[](size_t pos) const;char operator[](size_t pos);void push_back(char ch);void append(const char* str);void append(size_t n, char ch);string append(const string str);void insert(size_t pos, char ch);void insert(size_t pos, const char* str);string operator(const char* str);string operator(char ch);string operator (const string str);void erase(size_t pos, size_t len npos);void swap(string s);size_t find(char ch, size_t pos 0);size_t find(const char* str, size_t pos 0);string substr(size_t pos 0, size_t len npos);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);}private:size_t _capacity 0;size_t _size 0;char* _str nullptr;const static size_t npos -1;};istream operator(istream in, string s);ostream operator(ostream out, const string s); }.cpp文件 #define _CRT_SECURE_NO_WARNINGS 1 //这个是声明和定义分离的版本 #includestring16.hnamespace Mystring {//构造函数string::string(const char* str){_size strlen(str);_capacity _size;_str new char[_capacity 1];strcpy(_str, str);}//析构函数string::~string(){delete[] _str;_str nullptr;_size 0;_capacity 0;}//拷贝构造 现代写法string::string(const string s){string tmp(s._str);swap(tmp);}//运算符重载string string::operator(string s){swap(s);return *this;}//仅能访问const char string::operator[](size_t pos) const{assert(pos _size);//assert 括号里为假的时候才会报错return _str[pos];}//访问修改char string::operator[](size_t pos){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::resize(size_t n, char c){if (n _size){if (n _capacity){reserve(n); }for (size_t i _size; i n; i){_str[i] c;}}else if (n _size){_size n;}_str[_size] \0;}void string::push_back(char ch){if (_size _capacity){size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] ch;_size;_str[_size] \0;}void string::append(const char* str){size_t len strlen(str);if (_size len _capacity){reserve(_size len);}strcpy(_str _size, str);_size len;}string string::append(const string str){// 检查是否需要扩展容量if (str._size _capacity - _size) // 判断是否需要扩容{reserve(_capacity str._size 1); // 扩容并预留足够空间}// 复制传入的字符串到当前字符串的末尾strcpy(_str _size, str._str); // _str _size 是当前字符串尾部// 更新当前字符串的大小_size str._size; // 更新_size// 手动设置字符串的结尾_str[_size] \0; // 手动设置字符串尾部的\0// 返回当前对象的引用return *this; // 返回string对象}void string::append(size_t n, char ch){// 检查是否需要扩展容量if (_size n _capacity){reserve(_size n); // 扩展容量以容纳新字符}// 将字符 ch 追加 n 次到字符串末尾for (size_t i 0; i n; i){_str[_size i] ch;}// 更新字符串的大小_size n;// 确保字符串以 \0 结尾_str[_size] \0;}void string::insert(size_t pos, char ch){assert(pos _size);if (_size _capacity){size_t newCapacity _capacity 0 ? 4 : _capacity * 2;reserve(newCapacity);}/*int end _size;while (end (int)pos){_str[end 1] _str[end];--end;}*/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);}int end _size;while (end (int)pos){_str[end len] _str[end];--end;}strncpy(_str pos, str, len);_size len;}string string::operator(char ch){push_back(ch);return *this;}string string::operator(const char* str){append(str);return *this;}void string::erase(size_t pos, size_t len){assert(pos _size);if (len npos || pos len _size){_str[pos] \0;_size pos;}else{strcpy(_str pos, _str pos len);_size - len;}}void string::swap(string s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t string::find(char ch, size_t pos){for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* ptr strstr(_str pos, str);if (ptr nullptr){return npos;}else{return ptr - _str;}}string string::substr(size_t pos, size_t len){assert(pos _size);size_t end pos len;if (len npos || pos len _size){end _size;}string str;str.reserve(end - pos);for (size_t i pos; i end; i){str _str[i];}return str;}ostream operator(ostream out, const string s){for (auto ch : s){out ch;}return out;}istream operator(istream in, string s){s.clear();char buff[128] { 0 };char ch in.get();int i 0;while (ch ! ch ! \n){buff[i] ch;if (i 127){buff[i] \0;s buff;i 0;}ch in.get();}if (i 0){buff[i] \0;s buff;}return in;} } [ 声明 ] 由于作者水平有限本文有错误和不准确之处在所难免本人也很想知道这些错误恳望读者批评指正
http://www.eeditor.cn/news/119223/

相关文章:

  • 建设网站是什么模式衡量网站质量的标准
  • 南宁网站怎么制作公司个人网站服务器推荐
  • 网络营销的作用安卓优化大师app下载
  • 做牛仔的时尚网站简述seo的概念
  • H5平台网站建设已有网站做移动网站
  • 网站建设流量是怎么回事杭州做网站哪家公司好
  • 网站如何开通微信支付接口140平米装修全包费用
  • 肃宁县做网站价格做网站流量怎么卖
  • 四川省住房和城乡建设网站网站框架怎么做
  • 深圳电子商务网站建设wordpress内部跳转链接
  • 东莞快速网站制作哪家强番禺建设网站系统
  • 盘锦做网站选哪家网站开发要计入无形资产吗
  • 服装网站广州公司电商网站建设
  • wordpress 淘宝客app襄樊seo排名
  • 车辆管理网站开发公司官网静态
  • 山西城乡建设厅网站首页wordpress添加搜索框
  • 制作公司网站视频天眼网
  • 石碣镇网站仿做揭阳网站开发定制
  • 福田网站建设罗湖网站建设网站建设需要会什么
  • 电子商务网站建设的规划方案做国际网站每年要多少钱
  • 邯郸市建设局官方网站有效的网站优化
  • 网站虚拟主机购买教程网站轮换图
  • 国家级示范校建设专题网站网站结构分类
  • 局域网多网站建设网站更改备案信息在哪
  • 网站建设开封软件制作广州建外贸网站
  • html5制作网站响应式网站 768 320
  • 接私活做网站要不要签合同西青区个人网页设计制作软件
  • 建筑效果图网站有哪些thinkphp做直播网站
  • 网站运营优化培训做视频网站公司要怎么做
  • 兼职网站项目建设报告(完整版)百度大搜推广和百度竞价