目录

引言
在C++ 编程世界里, string 类是处理字符串的重要工具。标准库中的 string 类功能强大且使用便捷,但深入理解其底层实现原理,对提升C++ 编程能力大有裨益。今天,我们就来全面模拟实现一个功能较为完备的 String 类,涵盖构造、拷贝、赋值、析构以及丰富的字符串操作等功能。
一、基础框架搭建
成员变量与基本构造函数
cpp
class String {
private:
char* _str; // 用于存储字符串
public:
// 构造函数,默认构造空字符串
String(const char* str = "") {
if (str == nullptr) {
// 若传入指针为空,视为错误情况,这里简单断言并处理
assert(false);
_str = new char[1];
*_str = '\0';
return;
}
// 计算字符串长度并分配内存
size_t len = strlen(str);
_str = new char[len + 1];
// 复制字符串内容
strcpy(_str, str);
}
上述构造函数处理了传入正常字符串以及空指针的情况,为对象正确初始化字符串存储空间。
析构函数
cpp
~String() {
// 释放字符串占用的动态内存
if (_str) {
delete[] _str;
_str = nullptr;
}
}
析构函数负责在对象生命周期结束时,释放动态分配的内存,防止内存泄漏。
二、拷贝与赋值操作
深拷贝的拷贝构造函数
cpp
// 拷贝构造函数,采用深拷贝方式
String(const String& s) {
size_t len = strlen(s._str);
_str = new char[len + 1];
strcpy(_str, s._str);
}
深拷贝保证了新创建的对象拥有独立的字符串副本,与原对象互不干扰,避免了浅拷贝可能引发的资源共享和释放问题。
赋值运算符重载
cpp
// 赋值运算符重载
String& operator=(const String& s) {
if (this!= &s) { // 避免自我赋值
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}
该实现先为新值分配内存,复制内容后再释放原字符串内存,保证了赋值操作的正确性和安全性。同时处理了自我赋值的情况,防止错误发生。
三、字符串操作功能实现
获取字符串长度
cpp
size_t size() const {
return strlen(_str);
}
通过调用标准库的 strlen 函数,返回当前字符串的长度。
字符串拼接
cpp
String operator+(const String& s) const {
size_t len1 = strlen(_str);
size_t len2 = strlen(s._str);
char* result = new char[len1 + len2 + 1];
strcpy(result, _str);
strcat(result, s._str);
String newStr(result);
delete[] result;
return newStr;
}
该函数实现了两个 String 对象的拼接操作。先计算拼接后字符串的总长度,分配足够内存,然后依次复制两个字符串内容,最后返回新的 String 对象。
字符串比较
cpp
int compare(const String& s) const {
return strcmp(_str, s._str);
}
利用标准库的 strcmp 函数实现字符串的字典序比较,返回值小于0表示当前字符串小于传入字符串,等于0表示两者相等,大于0表示当前字符串大于传入字符串。
字符访问
cpp
char& operator[](size_t index) {
assert(index < strlen(_str));
return _str[index];
}
const char& operator[](size_t index) const {
assert(index < strlen(_str));
return _str[index];
}
提供了像数组一样通过下标访问字符的功能,同时区分了常量对象和非常量对象的访问方式,保证了操作的安全性和正确性。
四、迭代器相关实现(简单模拟)
迭代器类型定义
cpp
typedef char* iterator;
typedef const char* const_iterator;
定义了普通迭代器和常量迭代器类型,方便对字符串进行遍历操作。
迭代器获取函数
cpp
iterator begin() {
return _str;
}
iterator end() {
return _str + strlen(_str);
}
const_iterator begin() const {
return _str;
}
const_iterator end() const {
return _str + strlen(_str);
}
这些函数分别返回字符串起始和结束位置的迭代器,支持通过迭代器对字符串进行遍历,如:
cpp
String s("hello");
for (String::iterator it = s.begin(); it!= s.end(); ++it) {
std::cout << *it;
}
完整代码
#define _CRT_SECURE_NO_WARNINGS
#include<assert.h>
#include<iostream>
#include<string>
using namespace std;
namespace ldg
{
class string
{
public:
/*friend ostream& operator<<(ostream& out, const string& s);
friend istream& operator>>(istream& in, string& s);*/
typedef char* iterator;
public:
/*string(const char* str="")
{
_size = strlen(str);
_capacity = _size;
strcpy(_str, str);
}*/
string(const char* str)
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string()
{
_size=0;
_capacity=0;
_str = new char[1];
_str[0] = '\0';
}
string(const string& s)
{
_str = new char[s._capacity + 1];
memcpy(_str,s._str,s._size+1);
_size = s._size;
_capacity = s._capacity;
}
/*string& operator=(const string& s)
{
if (*this != s)
{
delete[]_str;
_str= new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
return *this;
}*/
/* string& operator=(const string& s)
{
if (*this != s)
{
string tmp(s);
swap(tmp);
}
return *this;
}*/
string& operator=( string tmp)
{
swap(tmp);
return *this;
}
~string()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
//////////////////////////////////////////////////////////////
// iterator
iterator begin(){
return _str;
}
iterator end(){
return _str + _size;
}
iterator begin()const{
return _str;
}
iterator end()const{
return _str + _size;
}
/////////////////////////////////////////////////////////////
// modify
void emcapacity(int newcapacity)
{
if (newcapacity <= _capacity)return;
char* newstr = new char[newcapacity];
strcpy(newstr, _str);
delete[]_str;
_str = newstr;
_capacity = newcapacity;
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* newstr = new char[n];
strcpy(newstr, _str);
delete[]_str;
_str = newstr;
_capacity = n;
}
}
void push_back(char c)
{
if (_size >= _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size++] = c;
_str[_size] = '\0';
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size >= _capacity)
{
reserve(_capacity + len);
}
strcpy(_str+_size, str);
_size += len;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
void clear()
{
_size = 0;
_str[0] = '\0';
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
const char* c_str()const
{
return _str;
}
/////////////////////////////////////////////////////////////
// capacity
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
bool empty()const
{
return _size == 0;
}
void resize(size_t n, char c = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else {
reserve(n);
/*if (n > _capacity)
{
emcapacity(n);
}*/
for (size_t i = _size;i < n;i++)_str[i] = c;
_size = n;
_str[_size] = '\0';
}
}
/* void reserve(size_t n)
{
if (n > _capacity)
{
emcapacity(n);
}
}*/
/////////////////////////////////////////////////////////////
// access
char& operator[](size_t index)
{
assert(index < strlen(_str));
return _str[index];
}
const char& operator[](size_t index)const
{
assert(index < strlen(_str));
return _str[index];
}
/////////////////////////////////////////////////////////////
//relational operators
bool operator<(const string& s)
{
/* size_t i1;
size_t i2;
while(i1 < _size&& i2 < s._size)
{
if (_str[i1] < s._str[i2])return true;
if (_str[i1] > s._str[i2])return false;
else {
i1++, i2++;
}
}
return i1 == _size && i2 != s._size;*/
bool ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
return ret == 0 ? _size < s._size : ret;
}
bool operator==(const string& s)
{
return _str&&s._str&&memcmp(_str, s._str, _size)==0;
}
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this<s);
}
bool operator!=(const string& s)
{
return !(*this == s);
}
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
assert(pos <= _size);
for (size_t i = pos;i < _size;i++)
{
if (_str[i] == c)
{
return i;
}
}
return std::string::npos;
}
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
assert(pos <= _size);
const char* ret = std::strstr(_str+pos, s);
if (ret)return ret - _str;
return std::string::npos;
}
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos,size_t n, char c)
{
assert(pos <= _size);
if (_size + n >= _capacity)reserve(_size + n);
int end = _size;
while (end >= (int)pos)
{
_str[end+n] = _str[end + 1];
end--;
}
for (int i = 0;i < n;i++)
{
_str[pos + i] = c;
}
_size += n;
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity) reserve(_size + len+1);
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
end--;
}
for (int i = 0;i < len;i++)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len)
{
if (pos > _size)return *this;
if (pos + len > _size)len = _size - pos;
for (size_t i = pos;i < _size - len;i++)_str[i] = _str[i + len];
_size -= len;
_str[_size] = '\0';
return *this;
}
private:
size_t _size;
size_t _capacity;
char* _str;
};
ostream& operator<<(ostream& out, const ldg::string& s)
{
for (int i = 0;i < s.size();i++)
{
out << s[i];
}
return out;
}
//istream& operator>>(istream& in, string& s)
//{
// s.clear();
// char ch = in.get();
// while (ch == ' ' || ch == '\0')
// {
// ch = in.get();
// }
// char buff[128];
// int i = 0;
// while (ch != ' ' && ch != '\0')
// {
// buff[i++] = ch;
// if (i == 127)
// {
// buff[i] = '\0';
// s += buff;
// i = 0;
// }
// ch = in.get();
// }
// if (i != 0)
// {
//
// s += buff;
// }
// return in;
//}
std::istream& operator>>(std::istream& in, ldg::string& s) {
s.clear();
std::string temp;
std::getline(in, temp);
size_t start = 0;
while (start < temp.size() && (temp[start] == ' ' || temp[start] == '\0')) {
start++;
}
size_t i = start;
while (i < temp.size() && temp[i] != ' ' && temp[i] != '\0') {
i++;
}
std::string subStr = temp.substr(start, i - start);
// 将std::string转换为const char*
s += subStr.c_str();
return in;
}
};
五、总结
通过以上一系列的实现,我们构建了一个功能相对丰富的 String 类模拟版本。从基本的对象生命周期管理(构造、析构),到关键的拷贝与赋值操作,再到多样化的字符串操作以及简单的迭代器支持,每一个部分都精心设计,确保正确性和高效性。理解这些底层实现原理,不仅能让我们更好地使用标准库中的 string 类,还能在面对复杂字符串处理需求时,具备更强的代码设计和优化能力。在实际编程中,我们可以根据具体场景进一步扩展和完善这个 String 类,使其发挥更大的作用。
777

被折叠的 条评论
为什么被折叠?



