目录
成员函数
3.4 修改操作
(3)insert()函数
(4)pop_back()函数
(5)erase()函数
(6)swap()函数
3.5 查找操作
(1)find()函数
(2)substr()函数
3.6 重载函数
(1)operator=赋值函数
(2)其他比较函数
(3)流插入和流提取
完整代码
结束语
第一篇链接:C++——string的模拟实现(上)
成员函数
3.4 修改操作
(3)insert()函数
insert() 函数用于在字符串的指定位置插入一个字符或字符串。
注意事项:
在移动字符串内容时,需要确保不会覆盖或丢失任何字符。
使用 memcpy() 时,要确保源和目标内存区域不重叠,否则可能导致未定义行为。
string.h:
// insert()函数
void insert(size_t pos, char ch); //插入字符
void insert(size_t pos, const char* str); //插入字符串
string.cpp:
// insert()函数
// 插入字符
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_capacity == _size)
{
size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newcapacity);
}
size_t end = _size + 1;
// 挪动元素
while (pos < end)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch; //将ch放置到pos这个位置
++_size; //更新_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);
}
// 从后向前移动字符串内容以腾出空间
for (size_t i = _size; i >= pos; --i)
{
_str[i + len] = _str[i];
}
// 复制新字符串到指定位置
memcpy(_str + pos, str, len);
// 添加新的字符串结束符
_str[_size + len] = '\0';
// 更新字符串长度
_size += len;
}
来个简单的代码测试一下:
(4)pop_back()函数
pop_back() 函数用于移除字符串末尾的字符。
实现:将字符串末尾的字符设置为 '\0'。 更新字符串长度 _size。
string.h:
// pop_back()函数
void pop_back();
string.cpp:
// pop_back()函数
void string::pop_back()
{
_str[_size - 1] = '\0';
--_size;
}
(5)erase()函数
erase() 函数用于从字符串中移除指定位置的字符或子字符串。
string.h:
// erase()函数
void erase(size_t pos = 0, size_t len = npos);
string.cpp:
// erase()函数
void string::erase(size_t pos,size_t len)
{
assert(pos < _size);
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0'; // 位置pos置为'\0'
_size = pos; // 有效元素个数为pos个
}
else // len小于后面的字符个数
{
// 将后面的字符拷贝到pos位置
strcpy(_str + pos, _str + pos + len);
_size -= len; // 更新有效元素
}
}
erase()需要额外定义一个类成员变量npos来实现,它为无符号数的-1,一般为整型的最大值。
string.h:
public:
static const size_t npos;
string.cpp:
const size_t string::npos = -1;
简单的对上面两个函数进行测试:
(6)swap()函数
swap() 函数用于交换两个字符串的内容。
string.h:
// swap()函数
void swap(string& str);
string.cpp:
// swap()函数
void string::swap(string& str)
{
// 这里我们调用std库中的swap()函数
std::swap(_str, str._str);
std::swap(_capacity, str._capacity);
std::swap(_size, str._size);
}
测试:
3.5 查找操作
(1)find()函数
find() 函数用于在字符串中查找指定字符或子字符串的位置。
我们在这里实现从指定位置开始向后遍历查找指定字符或者子字符串。
如果找到,则返回子字符串在字符串中的起始位置;否则返回 npos。
string.h:
// find()函数
size_t find(char ch, size_t pos);
size_t find(const char* str, size_t pos);
string.cpp:
// find()函数
// 寻找字符
size_t string::find(char ch, size_t pos)
{
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
// 从pos开始寻找字符串
size_t string::find(const char* str, size_t pos)
{
char* p = strstr(_str + pos, str);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
(2)substr()函数
substr() 函数用于获取字符串的子字符串。
string.h:
// substr()函数
string substr(size_t pos = 0, size_t len = npos);
string.cpp:
// substr()函数
string string::substr(size_t pos, size_t len)
{
string str;
if (len == npos || len >= _size - pos)
{
for (size_t i = pos; i < _size; i++)
{
str += _str[i];
}
}
else
{
for (size_t i = pos; i < pos + len; i++)
{
str += _str[i];
}
}
return str;
}
3.6 重载函数
在C++中,函数重载允许我们为同一函数名创建多个版本,这些版本可以有不同的参数列表(参数的数量或类型不同)。通过这种方式,我们可以使代码更加简洁和易于理解。
(1)operator=赋值函数
赋值运算符(operator=)用于将一个对象的内容复制到另一个对象中。对于自定义的字符串类,我们需要自己实现这个运算符,以确保正确地管理内存。
string.h:
// operator=赋值函数
string& operator=(const string& s);
string.cpp:
// operator= 函数
string& string::operator=(const string& str)
{
if (this != &str)
{
char* tmp = new char[str._capacity + 1];
strcpy(tmp, str._str);
delete[] _str;
_str = tmp;
_size = str._size;
_capacity = str._capacity;
}
return *this;
}
来测试一下:
这里我们可以对以上的代码进行优化:
// operator= 函数
string& string::operator=(const string& str)
{
if (this != &str)
{
string tmp(str._str); // 调用构造函数
swap(tmp); // 将tmp与this交换
}
return *this;
}
原始实现中,我们首先检查自赋值(即对象试图将自己赋值给自己),然后分配足够的内存来存储新字符串,复制内容,并释放旧内存。这种方法虽然有效,但可能会导致性能问题,特别是当字符串很长时,因为涉及到多次内存分配和释放。
优化后的实现采用了“拷贝-交换”技术。这种方法通过创建一个临时对象来存储要赋值的字符串,然后使用swap函数交换临时对象和当前对象的内容。由于swap函数通常实现得非常高效(只需交换指针),这种方法可以显著提高性能,并减少内存分配的次数。
(2)其他比较函数
每个比较运算符都基于strcmp函数的返回值来实现。
这些函数之间是可以复用的。
string.h:
// 比较函数
bool operator<(const string& s)const;
bool operator<=(const string& s)const;
bool operator>(const string& s)const;
bool operator>=(const string& s)const;
bool operator==(const string& s)const;
bool operator!=(const string& s)const;
string.cpp:
// 比较函数
bool string::operator<(const string& str) const
{
return strcmp(_str, str._str) < 0;
}
bool string::operator<=(const string& str) const
{
return *this < str || *this == str;
}
bool string::operator>(const string& str) const
{
return !(*this <= 0);
}
bool string::operator>=(const string& str) const
{
return !(*this <= str);
}
bool string::operator==(const string& str)const
{
return strcmp(_str, str._str) == 0;
}
bool string::operator!=(const string& str)const
{
return !(*this == str);
}
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
(3)流插入和流提取
我们接下来试着实现流插入和流提取:
注意,我们不能将这两个函数定义放在string类里
在 C++ 中,当我们使用 std::cout << d1 或 std::cin >> d1 这样的表达式时,左侧的 std::cout 或 std::cin 是对象,而右侧的 d1 是我们想要输出或输入的数据。运算符函数需要能够接受这两个对象作为参数。 如果 << 或 >> 运算符被定义为 std::string 的成员函数,那么它们将需要额外的 this 指针来访问类的成员变量,这将导致需要三个参数(this 指针,左操作数,右操作数),这与标准的运算符用法不兼容。
string.h:
//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);
string.cpp:
// 流输入
istream& operator>>(istream& is, string& str)
{
// 清除字符串 str 中的现有内容
str.clear();
char ch;
// 从输入流 is 中读取第一个字符到 ch 中
ch = is.get();
// 定义一个字符数组(缓冲区)buff
char buff[128];
size_t i = 0;
// 循环读取字符,直到遇到空格或换行符为止
while (ch != ' ' && ch != '\n')
{
// 将字符 ch 存储到缓冲区 buff 的当前位置
buff[i++] = ch;
if (i == 127)
{
// 在缓冲区末尾添加字符串结束符 '\0'
buff[i++] = '\0';
str += buff;
// 重置索引 i,为下一轮存储字符做准备
i = 0;
}
// 从输入流中读取下一个字符到 ch 中
ch = is.get();
}
// 循环结束后,检查缓冲区中是否还有未处理的字符
if (i > 0)
{
// 在缓冲区末尾添加字符串结束符 '\0'
buff[i] = '\0';
// 将缓冲区的内容追加到字符串 str 中
str += buff;
}
return is;
}
// 流提取
ostream& operator<<(ostream& os, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
测试一下:
完整代码
string.h:
#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;
namespace My_string
{
class string
{
public:
typedef char* iterator; //将char*重命名为iterator
typedef const char* const_iterator;
//const版本的iterator
const_iterator begin() const; //提供const_iterator begin()函数
const_iterator end() const; //提供const_iterator end()函数
//非const版本的iterator
iterator begin(); //提供iterator begin()函数
iterator end(); //提供iterator end()函数
string(const char* str = " "); //构造函数
~string(); //析构函数
string(const string& str); //拷贝构造函数
const char* c_str() const; // c_str()函数
size_t size() const; // size()函数
size_t capacity() const; // capacity()函数
bool empty() const; // empty()函数
// resize()函数
void resize(size_t n, char ch = '\0');
// 非const版本
char& operator[](size_t pos); //operator[]函数
// const版本
const char& operator[](size_t pos)const;
// 预留空间
void reserve(size_t n);
// 尾插一个字符
void push_back(char ch);
// 尾插一个字符串
void append(const char* str);
//operator+=函数可以构成重载,函数名相同,参数不同
string& operator+=(char ch); // 字符相加
string& operator +=(const char* str); // 字符串相加
// insert()函数
void insert(size_t pos, char ch); //插入字符
void insert(size_t pos, const char* str); //插入字符串
// erase()函数
void erase(size_t pos = 0, size_t len = npos);
// pop_back()函数
void pop_back();
// find()函数
size_t find(char ch, size_t pos);
size_t find(const char* str, size_t pos);
// substr()函数
string substr(size_t pos = 0, size_t len = npos);
// operator=赋值函数
string& operator=(const string& s);
// swap()函数
void swap(string& str);
// 比较函数
bool operator<(const string& s)const;
bool operator<=(const string& s)const;
bool operator>(const string& s)const;
bool operator>=(const string& s)const;
bool operator==(const string& s)const;
bool operator!=(const string& s)const;
// clear()函数
void clear();
public:
static const size_t npos;
private:
char* _str; // 指向字符串的指针
size_t _size; // 有效字符个数
size_t _capacity; // 有效空间个数
};
//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);
}
string.cpp:
#include"string.h"
namespace My_string
{
const size_t string::npos = -1;
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
// 构造函数
string::string(const char* str)
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1]; // +1用于储存'\0'
strcpy(_str, str);
}
// 析构函数
string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
// 拷贝构造函数(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 string& str)
//{
// string tmp(str._str);
// std::swap(tmp._str, _str);
// std::swap(tmp._size, _size);
// std::swap(tmp._capacity, _capacity);
//}
// 拷贝构造函数(3)
//string::string(const string& str)
//{
// string tmp(str._str);
// swap(tmp); // 这里的swap我们接下来会定义
//}
// c_str()函数
const char* string::c_str() const
{
return _str;
}
// size()函数
size_t string::size() const
{
return _size;
}
// capacity()函数
size_t string::capacity() const
{
return _capacity;
}
// empty()函数
bool string::empty() const
{
return _size == 0;
}
// operator[]函数
char& string::operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
// const版本
const char& string::operator[](size_t pos)const
{
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;
}
}
// resize()函数
void string::resize(size_t n, char ch)
{
if (n > _size)
{
if (n > _capacity)
{
reserve(n);
}
// 使用 memset 函数将字符 ch
// 填充到新添加的空间中
memset(_str + _size, ch, n - _size);
}
_size = n;
_str[n] = '\0';
}
//尾插一个字符
void string::push_back(char ch)
{
if (_capacity == _size)
{
size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
_size++;
}
//尾插一个字符串
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::operator+=(char ch)
{
// 调用push_back()函数
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
// insert()函数
// 插入字符
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_capacity == _size)
{
size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newcapacity);
}
size_t end = _size + 1;
// 挪动元素
while (pos < end)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch; //将ch放置到pos这个位置
++_size; //更新_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);
}
// 从后向前移动字符串内容以腾出空间
for (size_t i = _size; i >= pos; --i)
{
_str[i + len] = _str[i];
}
// 复制新字符串到指定位置
memcpy(_str + pos, str, len);
// 添加新的字符串结束符
_str[_size + len] = '\0';
// 更新字符串长度
_size += len;
}
// erase()函数
void string::erase(size_t pos,size_t len)
{
assert(pos < _size);
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0'; // 位置pos置为'\0'
_size = pos; // 有效元素个数为pos个
}
else // len小于后面的字符个数
{
// 将后面的字符拷贝到pos位置
strcpy(_str + pos, _str + pos + len);
_size -= len; // 更新有效元素
}
}
// pop_back()函数
void string::pop_back()
{
_str[_size - 1] = '\0';
--_size;
}
// find()函数
// 寻找字符
size_t string::find(char ch, size_t pos)
{
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
// 从pos开始寻找字符串
size_t string::find(const char* str, size_t pos)
{
char* p = strstr(_str + pos, str);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
// substr()函数
string string::substr(size_t pos, size_t len)
{
string str;
if (len == npos || len >= _size - pos)
{
for (size_t i = pos; i < _size; i++)
{
str += _str[i];
}
}
else
{
for (size_t i = pos; i < pos + len; i++)
{
str += _str[i];
}
}
return str;
}
// operator= 函数
//string& string::operator=(const string& str)
//{
// if (this != &str)
// {
// char* tmp = new char[str._capacity + 1];
// strcpy(tmp, str._str);
// delete[] _str;
// _str = tmp;
// _size = str._size;
// _capacity = str._capacity;
// }
// return *this;
//}
// operator= 函数
string& string::operator=(const string& str)
{
if (this != &str)
{
string tmp(str._str); // 调用构造函数
swap(tmp); // 将tmp与this交换
}
return *this;
}
// swap()函数
void string::swap(string& str)
{
// 这里我们调用std库中的swap()函数
std::swap(_str, str._str);
std::swap(_capacity, str._capacity);
std::swap(_size, str._size);
}
// 比较函数
bool string::operator<(const string& str) const
{
return strcmp(_str, str._str) < 0;
}
bool string::operator<=(const string& str) const
{
return *this < str || *this == str;
}
bool string::operator>(const string& str) const
{
return !(*this <= 0);
}
bool string::operator>=(const string& str) const
{
return !(*this <= str);
}
bool string::operator==(const string& str)const
{
return strcmp(_str, str._str) == 0;
}
bool string::operator!=(const string& str)const
{
return !(*this == str);
}
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
// 流输入
istream& operator>>(istream& is, string& str)
{
// 清除字符串 str 中的现有内容
str.clear();
char ch;
// 从输入流 is 中读取第一个字符到 ch 中
ch = is.get();
// 定义一个字符数组(缓冲区)buff
char buff[128];
size_t i = 0;
// 循环读取字符,直到遇到空格或换行符为止
while (ch != ' ' && ch != '\n')
{
// 将字符 ch 存储到缓冲区 buff 的当前位置
buff[i++] = ch;
if (i == 127)
{
// 在缓冲区末尾添加字符串结束符 '\0'
buff[i++] = '\0';
str += buff;
// 重置索引 i,为下一轮存储字符做准备
i = 0;
}
// 从输入流中读取下一个字符到 ch 中
ch = is.get();
}
// 循环结束后,检查缓冲区中是否还有未处理的字符
if (i > 0)
{
// 在缓冲区末尾添加字符串结束符 '\0'
buff[i] = '\0';
// 将缓冲区的内容追加到字符串 str 中
str += buff;
}
return is;
}
// 流提取
ostream& operator<<(ostream& os, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
}
test.cpp:
#include"string.h"
#include<iostream>
using namespace std;
void test1()
{
// 测试构造函数
My_string::string s1("Hello");
cout << "s1: " << s1.c_str() << endl;
// 测试拷贝构造函数
/*My_string::string s2(s1);
cout << "s2 (s1): " << s2.c_str() << endl; */
}
void test2()
{
My_string::string str("Hello");
str[0] = 'h';
cout << str.c_str() << endl;
//使用operator[]函数打印
for (size_t i = 0; i < str.size(); i++)
{
cout << str[i] << " ";
}
cout << endl;
}
void test3()
{
My_string::string str("hello");
for (auto i : str)
{
cout << i << " ";
}
cout << endl;
My_string::string::iterator it1 = str.begin();
while (it1 != str.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
My_string::string::iterator it2 = str.end();
if (it2 != str.begin())
{ // 检查避免直接解引用 end()
--it2; // 先移动到一个有效的位置
while (it2 != str.begin())
{
std::cout << *it2 << " ";
--it2;
}
std::cout << *it2 << " "; // 输出最后一个字符(begin() 之前的字符)
}
std::cout << std::endl;
}
void test4()
{
My_string::string str("hello");
cout << str.c_str() << endl;
str.push_back('A');
cout << str.c_str() << endl;
str.append("world");
cout << str.c_str() << endl;
str += 'C';
cout << str.c_str() << endl;
}
void test5()
{
My_string::string str("hello world");
str.insert(6, 'x');
cout << str.c_str() << endl;
str.insert(6, "yyyyy");
cout << str.c_str() << endl;
str.insert(0, 'X');
cout << str.c_str() << endl;
}
void test6()
{
My_string::string str("hello world");
str.pop_back();
cout << str.c_str() << endl;
str.erase(2, 3);
cout << str.c_str() << endl;
}
void test7()
{
My_string::string str("hello world");
cout << str.find('l', 0) << endl;
cout << str.find("ld", 0) << endl;
}
void test8()
{
My_string::string str1("hello");
My_string::string str2("world");
cout << str1.c_str() << endl;
cout << str2.c_str() << endl;
str1.swap(str2);
cout << str1.c_str() << endl;
cout << str2.c_str() << endl;
}
void test9()
{
My_string::string str("hello World");
cout << str.substr(2, 2) << endl;
cout << str.substr(7, 3) << endl;
}
void test10()
{
My_string::string str1("hello world");
My_string::string str2("xxxxxxxxxxx");
str1 = str2;
cout << str1.c_str() << endl;
cout << str2.c_str() << endl;
My_string::string str3("yyyyy");
str1 = str3;
cout << str1.c_str() << endl;
cout << str3.c_str() << endl;
}
void test11()
{
My_string::string str("hello");
str.resize(10, '*');
cout << str.c_str() << endl;
str.reserve(15);
cout << str.c_str() << endl;
}
void test12()
{
My_string::string str("XXX");
cin >> str;
cout << str << endl;
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
//test6();
//test7();
//test8();
//test9();
//test10();
//test11();
test12();
return 0;
}
结束语
实验周太忙啦!!!一直没时间写。。。
感谢各位大佬的阅读!!!
求点赞收藏评论关注!!!