当前位置:首页 » 《资源分享》 » 正文

【C++】string类——模拟实现

18 人参与  2024年09月06日 14:04  分类 : 《资源分享》  评论

点击全文阅读


头像 ?个人主页:奋斗的小羊 ?所属专栏:C++ 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

目录

前言?1、string类主要函数接口?2、string类的模拟实现?2.1 构造和析构?2.2 运算符重载?2.2.1 赋值重载?2.2.2 [ ]重载?2.2.3 +=重载?2.2.4 <<重载?2.2.5 >>重载?2.2.6 关系运算符重载 ?2.3 迭代器?2.4 扩容?2.5 插入?2.5.1 尾插?2.5.2 追加字符串?2.5.3 插入字符?2.5.4 插入字符串 ?2.6 删除?2.7 查找?2.7.1 查找字符?2.7.2 查找字符串?2.7.3 返回子串 ?3、string类模拟实现完整代码


前言

通过模拟实现string类的主要接口可以使我们对string类的理解更加透彻,深入理解内存管理,可以更好地理解字符串在内存中的存储方式,以及如何进行内存分配和释放,从而避免常见的内存泄漏和溢出问题,加深对面向对象编程理念的理解,比如封装、继承和多态。


?1、string类主要函数接口

模拟实现string类,主要是实现string类的构造、拷贝构造、运算符重载、析构等。
为了防止与标准库中string类命名冲突,我们在空间域yjz中来模拟实现我们的string类。

namespace yjz{class string{public:typedef char* iterator;typedef const char* const_iterator;//无参构造string(){}//带参构造string(const char* str = "")//合二为一为缺省参数,空字符串表示'\0'{}//拷贝构造string(const string& str){}//赋值重载string& operator=(const string& str){}//析构~string(){}size_t size() const{}size_t capacity() const{}//清理数据void clear(){}//返回C格式const char* c_str() const{}char& operator[](size_t n)//可修改{}const char& operator[](size_t n) const//常量{}//迭代器iterator begin(){}iterator end(){}const_iterator begin() const{}const_iterator end() const{}void reserve(size_t n = 0);//扩容void push_back(char ch);//尾插void append(const char* str);//追加字符串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);//删除size_t find(char ch, size_t pos = 0) const;//返回字符位置size_t find(const char* str, size_t pos = 0) const;//返回字符串位置string substr(size_t pos, size_t len = npos) const;//截取n个字符返回private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;static const size_t npos;};//实现成全局函数,支持字符串和string对象比较bool operator<(const string& s1, const string& s2);bool operator<=(const string& s1, const string& s2);bool operator>(const string& s1, const string& s2);bool operator>=(const string& s1, const string& s2);bool operator==(const string& s1, const string& s2);bool operator!=(const string& s1, const string& s2);ostream& operator<<(ostream& out, const string& str);istream& operator>>(istream& in, string& str);}

?2、string类的模拟实现

?2.1 构造和析构

无参构造和带参构造可以合为一个默认构造,缺省参数不能给nullptr,可以给空字符串" ",用'\0'初始化_capacity不包含'\0',每次开空间都要多开一个给被拷贝构造的对象新开一块空间,用strcpy将原字符串拷贝给新对象
无参构造//string()//:_str(new char[1]{'\0'})//,_size(0)//,_capacity(0)//{}//带参构造string(const char* str = "")//合二为一为缺省参数,空字符串表示'\0'{_size = strlen(str);_capacity = _size;_str = new char[_size + 1];//多开一个存'\0'strcpy(_str, str);//strcpy把'\0'也拷贝}//拷贝构造string(const string& str){_str = new char[str._capacity + 1];//多开一个存'\0'strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}//析构~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}void clear(){_str[0] = '\0';_size = 0;}//返回C格式const char* c_str() const{return _str;}

| 拷贝构造的现代写法:

上面实现的拷贝构造函数是我们自己申请一块新空间,再将数据拷贝过来,是比较传统的写法。
除了自己申请空间外还有一种办法也可以完成拷贝构造,就是借助构造函数+swap函数掠夺,间接完成拷贝构造。

void swap(string& tmp){std::swap(_str, tmp._str);std::swap(_size, tmp._size);std::swap(_capacity, tmp._capacity);}//拷贝构造(现代写法)//string s2 = s1;string(const string& str){string tmp(str._str);//构造swap(tmp);}
我们想用string类对象s1拷贝构造一个新对象s2,可以用构造函数构造一个临时对象tmp,然后再让s2通过swap函数掠夺tmp的所有数据,最后释放掉tmp_str需要在声明的位置给缺省值nullptr

?2.2 运算符重载

?2.2.1 赋值重载

编译器默认生成的赋值重载完成的是浅拷贝,还是和拷贝构造一样的问题,同一块空间会析构两次,所以需要我们自己实现赋值重载,完成深拷贝先释放掉旧空间,申请一块新空间往新空间内赋值,重新申请一块空间而不在原空间内直接赋值的原因是赋值的两个对象大小可能不一样,扩容比较麻烦还要考虑自己给自己赋值的情况,虽然我们不这么干,但是语法上是允许的参数和返回值我们都用引用,可以减少拷贝提高效率
string& operator=(const string& str){//防止自己给自己赋值if (this != &str){delete[] _str;_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}return *this;}

| 赋值重载的现代写法:

赋值重载和拷贝构造类似,所以赋值重载也可以用类似拷贝构造的现代写法
//赋值重载(现代写法)//s2 = s1;string& operator=(const string& str){//防止自己给自己赋值if (this != &str){//string tmp(str._str);string tmp(str);//构造和拷贝构造都可swap(tmp);}return *this;}
我们想让s2指向一块和s1一样大小的空间,再把s1的内容赋值给s2,也是可以构造 / 拷贝构造一个临时对象tmp,让s2通过swap函数掠夺tmp的所有数据tmp出作用域后自动析构,s2tmp交换了所以此时析构的是s2原来指向的空间,因为s2原来指向的空间本来就要析构所以一举两得

虽然上面的做法很绝,但是还有更绝的,我们直接一步做到位:

//赋值重载(现代写法)string& operator=(string tmp){swap(tmp);return *this;}
不用传引用,直接传值,因为传值传参会调用拷贝构造

?2.2.2 [ ]重载

char& operator[](size_t n)//可修改{assert(n < _size);return _str[n];}const char& operator[](size_t n) const//常量{assert(n < _size);return _str[n];}

?2.2.3 +=重载

string& string::operator+=(char ch)//尾插{push_back(ch);return *this;}string& string::operator+=(const char* str)//追加字符串{append(str);return *this;}

?2.2.4 <<重载

流插入、流提取重载放到全局,不需要定义为友元函数,因为没有访问成员变量,只访问的成员函数
ostream& operator<<(ostream& out, const string& str){for (auto ch : str){out << ch;}return out;}

?2.2.5 >>重载

从输入流中提取一个字符串,将序列存储在str中,该序列将被覆盖istream提取操作使用空格作为分隔符
istream& operator>>(istream& in, string& str){char ch;in >> ch;while (ch != ' ' && ch != '\n'){str += ch;in >> ch;}return in;}

上面的流提取重载是存在问题的,我们期望的是读到空格或者换行就结束,但是对于字符而言是不需要分割的,没有分隔符的概念,所以提取字符会跳过空格和换行,因次不能用复用库中的流提取。

为了能读到空格和换行字符,我们可以使用get调用clear清空原始数据,但不改变空间大小
istream& operator>>(istream& in, string& str){str.clear();char ch;//cin >> ch;ch = in.get();while (ch != ' ' && ch != '\n'){str += ch;//in >> ch;ch = in.get();}return in;}

| >>重载优化

上面我们实现的>>重载虽然没什么绝对的问题,但是存在缺陷。如果输入的字符串太长,不断+=要扩容很多次,消耗还是比较大的。虽然可以提前扩容解决,但是到底扩容多大又是个问题。
为了减少拷贝,我们可以模拟一个缓冲区。先在栈上开一个大小适中的数组(理论上越大越好,实际几百比较合适),将读到的字符先放到数组中,等数组满了再拷贝到string类对象中,这样可以大大减少堆上空间的扩容次数。
虽然在栈上开了空间,但是在栈上开空间高效且消耗小,出作用域自动销毁。

istream& operator>>(istream& in, string& str){str.clear();char ch;const size_t N = 256;char buff[N];size_t i = 0;ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';str += buff;}return in;}
模拟缓冲区,可以将扩容次数和空间消耗降到最小

?2.2.6 关系运算符重载

我们只需要写其中的两个,剩下的可以复用
bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}
库中写了类和类、类和字符串、字符串和类比较,我们只写了类和类比较但也能完成字符串和类比较是因为隐式类型转换(但是类型转换中间会构造一个临时对象,再用这个临时对象去拷贝构造,编译器优化为直接构造

?2.3 迭代器

普通迭代器
typedef char* iterator;iterator begin(){return _str;//指向第一个字符}iterator end(){return _str + _size;//指向'\0'}
常量迭代器
typedef const char* const_iterator;const_iterator begin() const{return _str;//指向第一个字符}const_iterator end() const{return _str + _size;//指向'\0'}

?2.4 扩容

大了扩容,小了不变手动异地扩容多开一个空间用于存字符'\0'拷贝完原始数据后手动释放旧空间
void string::reserve(size_t n)//【异地扩容】{if (n > _capacity){char* tmp = new char[n + 1];//多开一个存'\0'strcpy(tmp, _str);delete[] _str;_str = tmp;tmp = nullptr;_capacity = n;//不包含'\0'}}

?2.5 插入

?2.5.1 尾插

如果空间不够,可以调用reserve来扩容尾插一个字符后,不要忘了++_size因为尾插的字符覆盖了字符'\0',所以我们还要在字符串末尾手动加上'\0'
void string::push_back(char ch)//尾插{if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';//尾插完后不要忘了补上'\0'}

?2.5.2 追加字符串

追加字符串之前,我们要算一下追加后的字符串长度如果长度大于二倍的原空间,就按字符串长度扩;如果小于二倍的原空间,就按二倍扩容扩容完用strcpy拷贝追加的字符串时,要追加到_str + _size的位置追加完成后及时更新_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;//更新_size}

?2.5.3 插入字符

这里有一个比较容易踩坑的点,像下面实现的函数,当我们在除了_size == 0位置插入字符外其他地方都是可以正常实现的,但是头插会陷入死循环。

void string::insert(size_t pos, char ch)//插入字符{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t end = _size;while (end >= pos){_str[end + 1] = _str[end--];}_str[pos] = ch;_str[_size + 1] = '\0';}

头插会失败的原因是:我们定义的end无符号整型,所以end始终都是不小于0的,有同学可能会说把end改为int类型不就好了?但是就算用int定义end,这个函数还是会陷入死循环。

因为pos是无符号整型,posend比较时pos会使end转换为无符号整型,然后再参与比较。

关于算数转换更多详细内容请跳转阅读 —> C语言(操作符)2

要解决这个问题可以强制类型转换,但更建议像下面这样修改:

void string::insert(size_t pos, char ch)//插入字符{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t end = _size + 1;while (end > pos){_str[end--] = _str[end - 1];}_str[pos] = ch;_size++;}

?2.5.4 插入字符串

插入字符串前也要先计算一下插入后字符串的总长度,如果长度大于二倍的原空间,就按字符串长度扩;如果小于二倍的原空间,就按二倍扩容写循环结束条件时要细心的画图确定,如果有空格字符也要挪动插入字符串和追加字符串不同,追加字符串可以调用strcpy函数,因为strcpy函数可以将'\0'也拷贝过来,而插入字符串不需要将'\0'插入进来,所以不适合使用strcpy,可以考虑循环插入
void string::insert(size_t pos, const char* str)//插入字符串{assert(pos <= _size);size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size > 2 * _capacity ? len + _size : 2 * _capacity);}size_t end = _size + len;while (end > pos + len - 1){_str[end--] = _str[end - len];}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}
其实插入一个字符和插入一个字符串本质上是一样的,不同的只是一些值的差异和细节的处理

?2.6 删除

string类里面声明函数时可以给一个缺省值npos,如果函数调用时只给一个实参则默认pos位置后面的内容全部删除函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值
void erase(size_t pos, size_t len = npos);//删除
npos我们也只能在.h文件中声明(声明在string类private成员变量下,为静态常量),.cpp文件中定义npos为无符号整型,-1的补码为全1,经过算数转换后就成了整型最大值
const size_t string::npos = -1;
擦除字符串值中从字符位置开始并跨越 len 字符的部分,如果内容太短或 len string::npos,则擦除直到字符串末尾的部分要分两种情况分别讨论len大于pos位置后面字符串的长度和小于后面字符串的长度删除后也要及时更新_size的值
void string::erase(size_t pos, size_t len)//删除{assert(pos < _size);if (len >= _size - pos)//pos后面全删{_str[pos] = '\0';_size = pos;}else//pos后面不全删{for (size_t i = pos; i < _size - len + 1; i++){_str[i] = _str[i + len];}_size -= len;}}

?2.7 查找

?2.7.1 查找字符

当只传一个实参时,默认从头开始往后查找当指定 pos 时,搜索仅包括位置 pos 处或位置后的字符,而忽略任何可能出现的包含 pos 之前字符的情况
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;//没找到则返回npos}

?2.7.2 查找字符串

当只传一个实参时,默认从头开始往后查找可以调用strstr来完成,注意第一个参数不是_str,而是_str + pos如果没找到则返回npos,找到了则返回字符串第一个字符的下标,可以用ptr - _str两指针相减来获得下标
size_t string::find(const char* str, size_t pos) const//返回字符串位置{assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (nullptr == ptr){return npos;}else{return ptr - _str;}}

?2.7.3 返回子串

只传一个实参时默认返回pos位置后面的整个子串sub += _str[pos + i];是尾插一个字符,这一步骤不能写sub[i] = _str[pos + i];因为虽然运算符重载的operator[]可以返回指定位置的值,但是此时sub只是开了len个长度的空间,没有元素,_size为0,assert(pos < _size)会报错
string string::substr(size_t pos, size_t len) const//截取n个字符返回{assert(pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}

?3、string类模拟实现完整代码

string.h:

#pragma once#include <iostream>#include <assert.h>using namespace std;namespace yjz{class string{public:typedef char* iterator;typedef const char* const_iterator;无参构造//string()//:_str(new char[1]{'\0'})//,_size(0)//,_capacity(0)//{}//带参构造string(const char* str = "")//合二为一为缺省参数,空字符串表示'\0'{_size = strlen(str);_capacity = _size;_str = new char[_size + 1];//多开一个存'\0'strcpy(_str, str);//strcpy把'\0'也拷贝}//拷贝构造(传统写法) 1//string(const string& str)//{//_str = new char[str._capacity + 1];//多开一个存'\0'//strcpy(_str, str._str);//_size = str._size;//_capacity = str._capacity;//}void swap(string& tmp){std::swap(_str, tmp._str);std::swap(_size, tmp._size);std::swap(_capacity, tmp._capacity);}//拷贝构造(现代写法)string(const string& str){string tmp(str._str);//构造swap(tmp);}赋值重载(传统写法) 1//string& operator=(const string& str)//{////防止自己给自己赋值//if (this != &str)//{//delete[] _str;//_str = new char[str._capacity + 1];//strcpy(_str, str._str);//_size = str._size;//_capacity = str._capacity;//}//return *this;//}赋值重载(现代写法) 2//string& operator=(const string& str)//{//防止自己给自己赋值//if (this != &str)//{//string tmp(str._str);//string tmp(str);//构造和拷贝构造都可//swap(tmp);//}//return *this;//}//赋值重载(现代写法)string& operator=(string tmp){swap(tmp);return *this;}//析构~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}void clear(){_str[0] = '\0';_size = 0;}//返回C格式const char* c_str() const{return _str;}char& operator[](size_t n)//可修改{assert(n < _size);return _str[n];}const char& operator[](size_t n) const//常量{assert(n < _size);return _str[n];}//迭代器iterator begin(){return _str;//指向第一个字符}iterator end(){return _str + _size;//指向'\0'}const_iterator begin() const{return _str;//指向第一个字符}const_iterator end() const{return _str + _size;//指向'\0'}void reserve(size_t n = 0);//扩容void push_back(char ch);//尾插void append(const char* str);//追加字符串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);//删除size_t find(char ch, size_t pos = 0) const;//返回字符位置size_t find(const char* str, size_t pos = 0) const;//返回字符串位置string substr(size_t pos, size_t len = npos) const;//截取n个字符返回private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;static const size_t npos;};//实现成全局函数,支持字符串和string对象比较bool operator<(const string& s1, const string& s2);bool operator<=(const string& s1, const string& s2);bool operator>(const string& s1, const string& s2);bool operator>=(const string& s1, const string& s2);bool operator==(const string& s1, const string& s2);bool operator!=(const string& s1, const string& s2);ostream& operator<<(ostream& out, const string& str);istream& operator>>(istream& in, string& str);}

string.cpp:

#define  _CRT_SECURE_NO_WARNINGS#include "string.h"namespace yjz{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;//不包含'\0'}}void string::push_back(char ch)//尾插{if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';//尾插完后不要忘了补上'\0'}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;//更新_size}string& string::operator+=(char ch)//尾插{push_back(ch);return *this;}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 : 2 * _capacity);}size_t end = _size + 1;while (end > pos){_str[end--] = _str[end - 1];}_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str)//插入字符串{assert(pos <= size());int length = strlen(str);if (size() + length > capacity()){reserve(size() + length > 2 * capacity() ? size() + length : 2 * capacity());}for (size_t i = size(); i >= pos; i--){_str[i + length] = _str[i];}for (size_t i = 0; i < length; i++){_str[i + pos] = *(str + i);}_size += length;}void string::erase(size_t pos, size_t len)//删除{assert(pos < _size);if (len >= _size - pos)//pos后面全删{_str[pos] = '\0';_size = pos;}else//pos后面不全删{for (size_t i = pos; i < _size - len + 1; i++){_str[i] = _str[i + len];}_size -= len;}}size_t string::find(char ch, size_t pos) const//返回字符位置{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;//没找到则返回npos}size_t string::find(const char* str, size_t pos) const//返回字符串位置{assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (nullptr == ptr){return npos;}else{return ptr - _str;}}string string::substr(size_t pos, size_t len) const//截取n个字符返回{assert(pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}ostream& operator<<(ostream& out, const string& str){for (auto ch : str){out << ch;}return out;}//istream& operator>>(istream& in, string& str)//{//str.clear();//char ch;////cin >> ch;//ch = in.get();//while (ch != ' ' && ch != '\n')//{//str += ch;////in >> ch;//ch = in.get();//}//return in;//}istream& operator>>(istream& in, string& str){str.clear();char ch;const size_t N = 256;char buff[N];size_t i = 0;ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';str += buff;}return in;}}

test.cpp:

#define  _CRT_SECURE_NO_WARNINGS#include "string.h"namespace yjz{void test_string1(){string s1;string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;for (size_t i = 0; i < s2.size(); i++){cout << s2[i] << " ";}cout << endl;string s3("hello world");string::iterator it = s3.begin();while (it != s3.end()){cout << *it << " ";++it;}cout << endl;}void test_string2(){string s1("hello world");s1.push_back('#');cout << s1.c_str() << endl;s1.append("Are you ok?");cout << s1.c_str() << endl;s1 += '%';cout << s1.c_str() << endl;s1 += "Are you ok?";cout << s1.c_str() << endl;s1.insert(1, '&');s1.insert(0, '*');cout << s1.c_str() << endl;s1.insert(6, "Are you ok?");cout << s1.c_str() << endl;s1.erase(1, 5);cout << s1.c_str() << endl;}void test_string3(){string s("hello world");size_t pos = s.find('l');string suffix = s.substr(pos, 6);cout << suffix.c_str() << endl;string s1("hello world");string s2 = s1;cout << s2.c_str() << endl;string s3("Are you ok?");s1 = s3;cout << s1.c_str() << endl;}void test_string4(){string s1("hello world");string s2;s2 = s1;cout << s1 << endl;cout << s2 << endl;}}int main(){yjz::test_string4();return 0;}


点击全文阅读


本文链接:http://zhangshiyu.com/post/156205.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1