网站收录查询方法,网站建设微信公众号文章,wordpress付费阅读主题,如何用ip做网站目录
一、类的定义
二、初始化销毁
1、构造函数
2、辨析三种定义
3、析构函数
三、赋值
1、拷贝构造函数
2、赋值运算符
四、成员访问 operator[ ]
五、比较大小判断相等
六、容量操作
1、size()
2、reserve
3、push_back
4、append
5、加等运算符…目录
一、类的定义
二、初始化销毁
1、构造函数
2、辨析三种定义
3、析构函数
三、赋值
1、拷贝构造函数
2、赋值运算符
四、成员访问 operator[ ]
五、比较大小判断相等
六、容量操作
1、size()
2、reserve
3、push_back
4、append
5、加等运算符
6、C风格
7、insert
插入字符
插入字符串
insert实现push_backappend
8、resize
9、erase
10、swap
11、find
七、迭代器
八、流输出流输入 在C中string类是一个非常重要的数据类型它提供了一系列的方法来处理字符串。然而你有没有想过string类是如何实现的呢在这篇文章中我们将模拟实现一个简化版的string类这可以更好地理解其内部工作原理。 一、类的定义
我们首先定义了一个名为bit::string的类它包含了一些私有成员变量和公有成员函数。
namespace bit
{class string{private:char* _str;size_t _capacity;size_t _size;static const size_t npos;//static const size_t npos -1;//也可以public:// ...};const size_t string::npos -1;
}在这个类中我们有三个私有成员变量_str_capacity和_size。_str是一个字符指针用于存储字符串的内容_capacity表示字符串的容量即可以存储的字符数量_size表示当前字符串的长度。此外我们还定义了一个静态常量npos它的值为-1通常用于表示“不存在”的位置。
二、初始化销毁
1、构造函数
string(const char* str ):_size(strlen(str))
{_capacity _size 0 ? 3 : _size;_str new char[_capacity 1];strcpy(_str, str);
} 构造函数string(const char* str )使用了全缺省的方式使用全缺省的构造函数可以有以下几种调用方式 不传递任何参数string s;这将创建一个空字符串的string对象。 传递一个空字符串string s();这将创建一个空字符串的string对象。 传递一个非空字符串string s(Hello);这将创建一个包含指定字符串的string对象。 传递一个空指针string s(nullptr);这将创建一个空字符串的string对象。 _size(strlen(str)): 这是一个初始化列表它将_size成员变量初始化为传入字符串str的长度。这是通过使用C标准库函数strlen来实现的。 _capaicty _size 0 ? 3 : _size;: 这行代码设置了_capacity成员变量的值。如果_size为0也就是说如果传入的字符串为空那么_capacity被设置为3。否则_capacity被设置为_size的值。 _str new char[_capaicty 1];: 这行代码在堆上为_str分配了足够的内存来存储字符串。分配的内存大小为_capacity 1额外的1用于存储字符串的空字符终止符\0。 strcpy(_str, str);: 这行代码将传入的字符串str复制到_str指向的内存中。这是通过使用C标准库函数strcpy来实现的。
2、辨析三种定义 string(const char* str nullptr) 不可以string(const char* str \0) 不可以string(const char* str \0) 可以 string(const char* str nullptr): 这个构造函数版本不可行因为nullptr不能被strlen和strcpy处理这会导致未定义的行为。 string(const char* str \0): 这个构造函数版本也不可行因为\0是一个字符而不是一个字符串。它不能被赋值给const char*类型的参数。 string(const char* str \0): 这个构造函数版本是可行的因为\0是一个包含空字符的字符串。然而它与string(const char* str )的行为是相同的因为在C中字符串总是以空字符终止。
3、析构函数 析构函数则负责释放_str指向的内存并将所有成员变量重置为初始状态。 ~string()
{delete[] _str;_str nullptr;_size _capaicty 0;
}三、赋值
1、拷贝构造函数 拷贝构造函数的目的是创建一个与参数对象s具有相同字符串和相同容量的新string对象。 string(const string s):_size(s._size), _capacity(s._capacity)
{_str new char[s._capacity 1];strcpy(_str, s._str);
} string(const string s): 这是复制构造函数的声明。它接受一个对已存在的字符串对象的引用作为参数。使用引用是为了避免无限递归因为如果不使用引用那么在创建新的字符串对象时会再次调用复制构造函数如此无限循环下去。 _size(s._size): 这是初始化列表的一部分它将新字符串对象的 _size 成员变量设置为传入的字符串对象的 _size。 _capaicty(s._capaicty): 同样是初始化列表的一部分它将新字符串对象的 _capaicty 成员变量设置为传入的字符串对象的 _capaicty。 _str new char[s._capaicty 1];: 这行代码为新字符串对象的 _str 成员变量分配内存。它创建一个新的字符数组大小为传入的字符串对象的 _capaicty 加 1。这里加 1 是为了留出空间存储字符串的结束字符 \0。 strcpy(_str, s._str);: 这行代码将传入的字符串对象的 _str 成员变量的内容复制到新字符串对象的 _str 成员变量中。
2、赋值运算符 这段代码是C中的赋值运算符重载函数用于将一个字符串对象赋值给另一个字符串对象。这也是一个深拷贝操作因为它创建了新的内存空间来存储复制的字符串而不是简单地复制指针。 string operator(const string s)
{if (this ! s){char* tmp new char[s._capaicity 1];strcpy(tmp, s._str);delete[] _str;_str tmp;_size s._size;_capaicity s._capacity;}return *this;
} string operator(const string s): 这是赋值运算符重载函数的声明。它接受一个对已存在的字符串对象的引用作为参数并返回对当前对象的引用以便支持链式赋值例如 a b c。 if (this ! s): 这是一个保护性检查用于防止自我赋值。如果尝试将一个对象赋值给它自己那么这个检查将阻止后续的代码执行。 char* tmp new char[s._capaicity 1];: 这行代码创建一个新的字符数组大小为传入的字符串对象的 _capaicity 加 1。这里加 1 是为了留出空间存储字符串的结束字符 \0。 strcpy(tmp, s._str);: 这行代码将传入的字符串对象的 _str 成员变量的内容复制到新创建的字符数组中。 delete[] _str;: 这行代码释放当前对象的 _str 成员变量所占用的内存。这是为了避免内存泄漏因为 _str 将被重新赋值。 _str tmp;: 这行代码将 _str 成员变量设置为新创建的字符数组的地址。 _size s._size;: 这行代码将当前对象的 _size 成员变量设置为传入的字符串对象的 _size。 _capaicity s._capaicity;: 这行代码将当前对象的 _capaicity 成员变量设置为传入的字符串对象的 _capaicity。 return *this;: 这行代码返回对当前对象的引用以支持连续赋值。
四、成员访问 我们为这个类重载了一些运算符以便可以像使用内置类型一样使用我们自己定义的string类。 operator[ ]
const char operator[](size_t pos) const
{assert(pos _size);return _str[pos];
}char operator[](size_t pos)
{assert(pos _size);return _str[pos];
} const char operator[](size_t pos) const: 这是一个重载的下标运算符它接受一个位置参数并返回该位置的字符的引用。这个版本的函数是常量版本它不能用于修改字符串的内容只能用于读取。如果位置超出字符串的大小它会触发一个断言错误。 char operator[](size_t pos): 这也是一个重载的下标运算符它接受一个位置参数并返回该位置的字符的引用。这个版本的函数可以用于修改字符串的内容。如果位置超出字符串的大小它也会触发一个断言错误。 使用[ ]时会自动选取合适的函数重载这两种重载版本的存在使得我们可以在保证类型安全的同时对常量和非常量字符串对象进行适当的操作。 五、比较大小判断相等 这些函数都是常量的这意味着它们不能修改字符串对象的状态。这些函数都接受一个对 string 对象的常量引用作为参数这可以避免不必要的复制操作。 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;return *this s || s *this;
}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);
} bool operator(const string s) const: 这个函数重载了大于运算符 。它使用 strcmp 函数比较两个字符串如果当前字符串大于参数字符串strcmp 会返回一个大于0的值所以函数返回 true。 bool operator(const string s) const: 这个函数重载了等于运算符 。它使用 strcmp 函数比较两个字符串如果两个字符串相等strcmp 会返回0所以函数返回 true。 bool operator(const string s) const: 这个函数重载了大于等于运算符 。它使用已经重载的 和 运算符来判断当前字符串是否大于或等于参数字符串。注释掉的代码有误应该是 return *this s || *this s;。 bool operator(const string s) const: 这个函数重载了小于运算符 。它使用已经重载的 运算符来判断当前字符串是否不大于等于参数字符串即小于参数字符串。 bool operator(const string s) const: 这个函数重载了小于等于运算符 。它使用已经重载的 运算符来判断当前字符串是否不大于参数字符串即小于或等于参数字符串。 bool operator!(const string s) const: 这个函数重载了不等于运算符 !。它使用已经重载的 运算符来判断当前字符串是否不等于参数字符串。
六、容量操作
1、size()
size_t size() const
{return _size;
}
size_t size() const: 这个函数返回字符串的大小即字符串中的字符数量。这个函数是常量函数这意味着它不能修改字符串的状态。
2、reserve
void reserve(size_t n)
{if (n _capacity){char* tmp new char[n 1];strcpy(tmp, _str);delete[] _str;_str tmp;_capacity n;}
} if (n _capacity): 检查新的容量 n 是否大于当前的容量 _capacity。如果不是那么函数立即返回不做任何操作。 char* tmp new char[n 1];: 如果新的容量大于当前的容量那么首先创建一个新的字符数组大小为新的容量加 1。这里加 1 是为了留出空间存储字符串的结束字符 \0。 strcpy(tmp, _str);: 然后将当前字符串的内容复制到新的字符数组中。 delete[] _str;: 然后释放当前字符串的内存。这是为了避免内存泄漏因为 _str 将被重新赋值。 _str tmp;: 然后将 _str 指向新的字符数组。这样_str 就指向了一个更大的内存区域可以存储更多的字符。 _capacity n;: 最后更新字符串的容量为新的容量。
3、push_back
void push_back(char ch)
{if (_size 1 _capacity){reserve(_capacity * 2);}_str[_size] ch;_size;_str[_size] \0;
}
void push_back(char ch): 这个函数在字符串的末尾添加一个字符。如果添加新字符后字符串的大小超过了当前的容量那么它会首先调用 reserve 函数来增加字符串的容量。然后它将新字符添加到字符串的末尾并更新字符串的大小最后不要忘记补充‘ \0 ’在新的字符串末尾。
4、append
void append(const char* str)
{size_t len strlen(str);if (_sizelen _capacity){reserve(_size len);}strcpy(_str _size, str);_size len;
}
void append(const char* str): 这个函数在字符串的末尾添加一个C风格字符串。它首先计算要添加的字符串的长度如果添加新字符串后字符串的大小超过了当前的容量那么它会首先调用 reserve 函数来增加字符串的容量。然后它将新字符串添加到当前字符串的末尾并更新字符串的大小。
5、加等运算符 这两个函数提供了一种简洁的方式来修改字符串的内容它们可以用来代 push_back 和 append 函数。 string operator(char ch)
{push_back(ch);return *this;
}string operator(const char* str)
{append(str);return *this;
} string operator(char ch): 这个函数接受一个字符作为参数并在字符串的末尾添加这个字符。它调用了 push_back 函数来完成这个操作。然后它返回对当前对象的引用以支持链式操作例如 str a b。 string operator(const char* str): 这个函数接受一个C风格字符串作为参数并在字符串的末尾添加这个字符串。它调用了 append 函数来完成这个操作。然后它也返回对当前对象的引用以支持链式操作例如 str hello world。
6、C风格
const char* c_str()
{return _str;
}
const char* c_str(): 这个函数返回一个指向字符串内部字符数组的指针。这个函数通常用于与需要C风格字符串的函数进行交互。返回类型为 const char*意味着你不能通过这个指针修改字符串的内容。
7、insert 这两个 insert 函数用于在 string 对象的指定位置插入字符或字符串。 插入字符 下面的代码有什么问题呢 void insert(size_t pos, char ch)
{assert(pos _size);if (_size 1 _capacity){reserve(2 * _capacity);}size_t end _size;while (end pos){_str[end 1] _str[end];--end;}_str[pos] ch;_size;
}
这段代码的主要问题在于 while 循环中的条件判断。在 C 中size_t 是无符号整数类型当 end 为 0 时--end 会导致 end 变为最大的无符号整数而不是 -1。因此当 pos 为 0 时end pos 的判断条件始终为 true这会导致无限循环。在这个无限循环中程序会尝试访问 _str[end]其中 end 是一个非常大的数。这将导致数组越界可能会引发运行时错误或者导致未定义的行为。 对上述代码进行改进 string insert(size_t pos, char ch)
{assert(pos _size);if (_size 1 _capacity){reserve(2 * _capacity);}size_t end _size 1;while (end pos){_str[end] _str[end-1];--end;}_str[pos] ch;_size;return *this;
} string insert(size_t pos, char ch): 这个函数在指定位置 pos 插入一个字符 ch。 首先它检查位置是否有效然后检查是否需要增加字符串的容量。 接下来它将从位置 pos 开始的所有字符向后移动一位为新字符腾出空间。 然后它在位置 pos 插入新字符并更新字符串的大小。 最后它返回对当前对象的引用以支持链式操作。
插入字符串
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 - 1){_str[end] _str[end - len];--end;}strncpy(_str pos, str, len);_size len;return *this;} string insert(size_t pos, const char* str): 这个函数在指定位置 pos 插入一个C风格字符串 str。 首先它检查位置是否有效然后计算要插入的字符串的长度然后检查是否需要增加字符串的容量。 然后它将从位置 pos 开始的所有字符向后移动 len 位为新字符串腾出空间。然后它在位置 pos 插入新字符串并更新字符串的大小。 最后它也返回对当前对象的引用以支持链式操作。
insert实现push_backappend
void push_back(char ch)
{//if (_size 1 _capacity)//{// reserve(_capacity * 2);//}//_str[_size] ch;//_size;//_str[_size] \0;insert(_size, ch);
}void append(const char* str)
{//size_t len strlen(str);//if (_sizelen _capacity)//{// reserve(_size len);//}//strcpy(_str _size, str);strcat(_str, str);//_size len;insert(_size, str);
}
8、resize
这个函数提供了一种灵活的方式来改变字符串的大小无论是增大还是减小。如果需要增大字符串的大小那么可以选择一个填充字符。如果需要减小字符串的大小那么将删除多余的字符。
void resize(size_t n, char ch \0)
{if (n _size){// 删除数据--保留前n个_size n;_str[_size] \0;}else if (n _size){if (n _capacity){reserve(n);}size_t i _size;while (i n){_str[i] ch;i;}_size n;_str[_size] \0;}
}
这个 resize 函数用于改变 string 对象的大小。它接受两个参数新的大小 n 和用于填充的字符 ch默认为 \0。 if (n _size): 如果新的大小 n 小于当前的大小 _size那么将删除多余的字符。这通过将 _size 设置为 n并在新的末尾位置添加结束字符 \0 来实现。 else if (n _size): 如果新的大小 n 大于当前的大小 _size那么将添加额外的字符。 if (n _capacity): 如果新的大小 n 大于当前的容量 _capacity那么将调用 reserve 函数来增加字符串的容量。while (i n): 然后使用一个循环来添加额外的字符。每个额外的字符都被设置为 ch。_size n;: 最后更新字符串的大小为新的大小 n并在新的末尾位置添加结束字符 \0。
9、erase
string erase(size_t pos, size_t len npos)
{assert(pos _size);if (len npos || pos len _size){_str[pos] \0;_size pos;}else{strcpy(_str pos, _str pos len);_size - len;}return *this;
} 函数定义: string erase(size_t pos, size_t len npos) 定义了一个名为 erase 的成员函数它属于某个 string 类型。它接受两个参数pos 和 len。pos 是要开始删除的位置索引从零开始而 len 是要删除的字符数。如果 len 没有指定默认值是 npos这通常表示字符串的最大长度意味着从 pos 开始删除到字符串末尾。 assert(pos _size); 这一行是一个调试时使用的断言确保 pos 小于当前字符串的大小 _size。如果 pos 大于等于 _size程序会在调试模式下终止。 检查 len 和删除操作: 如果 len 等于 npos 或 pos len 大于等于 _size意味着要删除的范围超出了字符串的末尾那么只需将位置 pos 处的字符设置为 \0空字符表示字符串在这里结束。然后更新 _size 为 pos相当于删除了从 pos 到字符串末尾的所有字符。如果不是上述情况则执行 else 里的代码。这里使用 strcpy(_str pos, _str pos len); 来删除指定的字符。这个操作实际上是将 pos len 之后的字符串复制到 pos 处从而覆盖掉要删除的部分。然后更新 _size 来反映新的字符串长度即减去删除的字符数。 返回值: 函数返回 *this即返回经过修改后的字符串对象的引用。这允许链式调用例如 str.erase(1, 3).append(abc);。
10、swap
通过使用std::swap函数它交换了两个对象的成员变量包括字符串内容、容量和大小。
void swap(string s)
{std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);
}
11、find
size_t find(char ch, size_t pos 0)
{assert(pos _size);for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;
}
size_t find(const char* str, size_t pos 0)
{assert(pos _size);// kmpchar* p strstr(_str pos, str);if (p nullptr){return npos;}else{return p - _str;}
}
这两个函数都是 string 类的 find 方法的实现用于在字符串中查找特定的字符或子字符串。我将逐步解释它们的作用和逻辑 函数定义size_t find(char ch, size_t pos 0) 和 size_t find(const char* str, size_t pos 0) 定义了两个重载的 find 函数。第一个函数用于查找单个字符 ch第二个函数用于查找子字符串 str。两个函数都接受一个可选的起始位置 pos默认值为 0表示从字符串的开始位置进行查找。 断言assert(pos _size); 这一行是一个调试时使用的断言确保 pos 小于当前字符串的大小 _size。如果 pos 大于等于 _size程序会在调试模式下终止。 查找字符在第一个 find 函数中从位置 pos 开始遍历字符串中的每个字符如果找到与 ch 相等的字符就返回其位置。如果遍历完整个字符串都没有找到就返回 npos通常表示字符串的最大长度这里用作查找失败的标志。 查找子字符串在第二个 find 函数中使用 strstr 函数从位置 pos 开始查找子字符串 str。strstr 是 C/C 标准库中的一个函数用于在一个字符串中查找另一个字符串的首次出现位置。如果找到strstr 返回第一次出现的位置的指针否则返回 nullptr。如果 strstr 返回 nullptr表示没有找到子字符串函数返回 npos。否则返回找到的位置即 p - _str。
七、迭代器
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;
} typedef char* iterator;: 这行代码定义了一个类型别名 iterator它是 char* 类型的别名。这意味着你可以使用 iterator 来代替 char*。 typedef const char* const_iterator;: 这行代码定义了一个类型别名 const_iterator它是 const char* 类型的别名。这意味着你可以使用 const_iterator 来代替 const char*。 iterator begin(): 这个函数返回一个指向字符串开始的迭代器。在这个 string 类中字符串的开始就是 _str 成员变量的地址。 iterator end(): 这个函数返回一个指向字符串结束的迭代器。在这个 string 类中字符串的结束就是 _str 成员变量的地址加上字符串的大小 _size。 const_iterator begin() const: 这是 begin() 函数的常量版本它返回一个指向常量字符串开始的常量迭代器。这个函数是常量的这意味着它不能修改字符串对象的状态。 const_iterator end() const: 这是 end() 函数的常量版本它返回一个指向常量字符串结束的常量迭代器。这个函数也是常量的。
八、流输出流输入 ostream operator(ostream out, const string s)这是输出流操作符的重载函数用于将string对象s输出到输出流out。 ostream operator(ostream out, const string s)
{for (auto ch : s){out ch;}return out;
}for (auto ch : s)这是一个范围for循环用于遍历字符串s中的每一个字符ch。out ch这行代码将字符ch输出到输出流out。return out最后函数返回输出流out以便进行链式操作。 在类中定义clear()成员函数用于清空 string 对象中的字符串。 void clear()
{_str[0] \0;size 0;
}istream operator(istream in, string s)这是输入流操作符的重载函数用于从输入流in读取数据到string对象s。 istream operator(istream in, string s)
{s.clear();char ch in.get();char buff[128];size_t i 0;while (ch ! ch ! \n){buff[i] ch;if (i 127){buff[127] \0;s buff;i 0;}ch in.get();}if (i ! 0){buff[i] \0;s buff;}return in;
} 首先函数接收两个参数一个是输入流对象in另一个是字符串s。这个函数的目的是从输入流in中读取字符然后将这些字符存储到字符串s中。 在函数开始时首先清空字符串s以确保它是空的。 然后使用in.get()函数从输入流中获取一个字符并将其存储在变量ch中。 定义一个字符数组buff用于临时存储从输入流中读取的字符。同时定义一个变量i用于跟踪buff中的当前位置。 接下来是一个循环该循环会一直执行直到从输入流中读取的字符是空格或换行符。在循环中首先将从输入流中读取的字符存储到buff中然后增加i的值。 如果i的值达到127即buff的最大容量则将buff的最后一个字符设置为\0表示字符串的结束然后将buff中的内容添加到字符串s中并将i重置为0。 在每次循环的结束都会从输入流中获取一个新的字符。 循环结束后如果buff中还有未添加到s的字符即i不等于0则将buff的最后一个字符设置为\0然后将buff中的内容添加到s中。 最后函数返回输入流对象in以便可以链式地进行更多的输入操作。