自定义CString类与MFC CString类接口对比
接口对比表格
功能分类 | 你的 CString 接口 | MFC CString 接口(ANSI) | 一致性 | 差异说明 |
构造函数 | CString() | CString() | 一致 | MFC 使用 LPCSTR(const char*),命名和参数逻辑相同。 |
析构函数 | ~CString() | ~CString() | 一致 | 均需手动释放内存(MFC 内部自动管理 COW 缓冲区)。 |
长度与判空 | int GetLength() const | int GetLength() const | 一致 | MFC 返回 BOOL(等效 bool),功能相同。 |
字符访问 | char GetAt(int) const | TCHAR GetAt(int) const | 一致 | MFC 使用 TCHAR(char in ANSI),越界处理:MFC 断言,你抛出异常。 |
修改字符 | void SetAt(int, char) | void SetAt(int, TCHAR) | 一致 | 参数和逻辑相同。 |
查找操作 | int Find(char) const | int Find(TCHAR) const | 一致 | MFC 参数为 LPCTSTR(const char*),功能相同。 |
替换操作 | int Replace(char, char) | int Replace(TCHAR, TCHAR) | 一致 | 均返回替换次数,字符串替换需动态扩容。 |
删除操作 | int Remove(char) | int Remove(TCHAR) | 一致 | 均返回删除次数,逻辑相同。 |
插入操作 | int Insert(int, char) | int Insert(int, TCHAR) | 一致 | 参数和返回值(新长度)相同,MFC 支持 CString 插入(通过 LPCTSTR 兼容)。 |
删除子串 | int Delete(int, int = 1) | int Delete(int, int = 1) | 一致 | 默认删除 1 个字符,越界处理逻辑需确保安全。 |
子串操作 | CString Left(int) const | CString Left(int) const | 一致 | 均返回子串对象,处理越界时返回空串。 |
赋值运算符 | CString& operator=(char) | CString& operator=(TCHAR) | 一致 | MFC 无非常量引用赋值,你已删除 operator=(CString&),与 MFC 一致。 |
追加运算符 | CString& operator+=(char) | CString& operator+=(TCHAR) | 一致 | 功能相同,MFC 通过 LPCTSTR 兼容 CString。 |
连接运算符 | friend CString operator+(...)(多重载) | friend CString operator+(...)(多重载) | 一致 | 均支持字符串连接,返回新对象。 |
比较运算符 | friend bool operator==/!=/>/<>=/<=(...) | friend BOOL operator==/!=/>/<>=/<=(...) | 一致 | MFC 返回 BOOL,逻辑相同;支持与 const char* 比较。 |
格式化 | void Format(const char*, ...) | void Format(LPCTSTR, ...) | 一致 | 均基于 sprintf,MFC 支持更多格式符(如 %I64d)。 |
大小写转换 | void MakeUpper() | void MakeUpper() | 部分一致 | MFC 支持本地化转换(如土耳其语),你仅实现 ASCII 转换。 |
去除空白字符 | void TrimLeft() | void TrimLeft() | 部分一致 | MFC 默认识别所有空白字符(如制表符),你的实现依赖 isspace(一致)。 |
反转字符串 | CString& MakeReverse() | CStringT& MakeReverse() | 一致 | MFC 通过 StringTraits::StringReverse 实现,你通过手动交换字符实现,功能相同。 |
内存管理接口 | char* GetBuffer(int) | LPTSTR GetBuffer(int) | 差异 | 你未提供 ReleaseBuffer(),需手动管理长度;MFC 通过 ReleaseBuffer() 自动更新。 |
高级功能 | 无 Tokenize/LoadString/ 路径处理等 | 支持 Tokenize/LoadString/ExtractFilePath 等 | 缺失 | MFC 提供字符串分词、资源加载、路径解析等高级功能。 |
安全检查 | 部分函数抛出异常(如越界) | 部分函数内部断言(如 ATLASSERT) | 差异 | MFC 依赖调试断言,你使用异常处理,均实现基本安全控制。 |
关键结论
1.基础操作完全一致:
构造 / 析构、查找 / 替换、比较 / 连接等核心接口的命名、参数和功能与 MFC 一致,可直接映射。
2.const 修饰完全对齐:
所有 const 成员函数、参数均符合 MFC 规范,类型安全无差异。
差异与缺失:
- 内存管理:缺少 ReleaseBuffer(),需手动处理 GetBuffer() 后的长度更新。
- 高级功能:无 Tokenize、资源加载、路径处理等 MFC 特有功能。
- 本地化:大小写转换未考虑区域设置,MFC 支持更全面。
适用场景:
- 一致场景:ANSI 环境下的基础字符串操作,如简单文本处理、非国际化逻辑。
- 不一致场景:需要 Unicode、高性能内存管理(COW)、复杂字符串处理(如路径解析)的场景。
cstring.h
#ifndef CSTRING_H
#define CSTRING_H#include <cstdarg>class CString
{
public://默认构造函数CString();//析构函数~CString();//从另一个 CString 复制构造CString(const CString& string1);// 从C风格字符串构造CString(const char *ch);// 从单个字符构造CString(const char ch);// 获取字符串长度int GetLength() const;// 判断字符串是否为空bool IsEmpty() const;// 清空字符串void Empty();// 获取指定位置的字符char GetAt(int iIndex) const;//重载 [] 运算符char& operator[](int iIndex);//重载 [] 运算符const char& operator[](int iIndex) const;//修改指定位置的字符void SetAt(int iIndex, char ch);// 查找字符最后一次出现的位置,失败返回-1int ReverseFind(char ch) const;// 替换所有指定字符,返回替换次数int Replace(char chOld, char chNew);// 替换所有子字符串,返回替换次数int Replace(const char* lpszOld, const char* lpszNew);// 删除所有指定字符,返回删除次数int Remove(char ch);// 在字符串的 nIndex 位置插入字符 ch,返回插入字符后字符串的长度int Insert(int nIndex, char ch);// 在字符串的 nIndex 位置插入字符串 lpsz,返回插入字符串后字符串的长度int Insert(int nIndex, char* lpsz);// 删除字符串中从 nIndex 位置开始的 nCount 个字符(默认删除 1 个字符),返回删除字符后字符串的长度int Delete(int nIndex, int nCount = 1);// 字符赋值运算符重载CString& operator=(char ch);// C风格字符串赋值运算符重载CString& operator=(const char * lpsz);// 常量CString拷贝赋值运算符重载CString& operator=(const CString& string1);// 字符追加运算符重载CString& operator+=(char ch);// C风格字符串追加运算符重载CString& operator+=(const char *str);// CString追加运算符重载CString& operator+=(const CString &Str);// 格式化字符串,功能类似sprintf,返回格式化后的长度void Format(const char* pstrFormat, ...);// 截取左边n个字符的子串CString Left(int nCount) const;// 截取右边n个字符的子串CString Right(int nCount) const;// 从nFirst开始到末尾的子串CString Mid(int nFirst) const;// 从nFirst开始截取nCount个字符的子串CString Mid(int nFirst, int nCount) const;// 获取内部缓冲区指针,用于直接操作内存char* GetBuffer(int nMinBufLength);// 查找int Find(char ch) const;int Find(const char * lpszSub) const;int Find(char ch, int nStart) const;int Find(const char *str, int uiBegin) const;int Find(CString sSub) const;int FindOneOf(const char* lpszCharSet) const;// 转换为大写void MakeUpper();// 转换为小写void MakeLower();// 去除左侧空白字符void TrimLeft();// 去除右侧空白字符void TrimRight();// 去除两侧空白字符void Trim();//反转CString & MakeReverse();//用于比较的函数int Compare(const char *str)const;int Compare(const CString &Str)const;int CompareNoCase(const char *str)const;int CompareNoCase(const CString &Str)const;// 连接两个CString对象,返回新对象friend CString operator+(const CString& string1, const CString& string2);friend CString operator+(const CString& string1, char ch);friend CString operator+(const CString& string1, const char* ch);friend CString operator+(const char *str, const CString &Str);friend CString operator+(char ch, const CString &Str);// 判断CString与C风格字符串是否相等friend bool operator==(const CString& string1, const char* ch);friend bool operator==(const CString& string1, const CString& string2);friend bool operator==(const char *str, const CString &Str);// 判断两个CString对象是否不相等friend bool operator!=(const CString& string1, const char* ch);friend bool operator!=(const CString& string1, const CString& string2);friend bool operator!=(const char *str, const CString &Str);// 比较两个CString对象大小(大于)friend bool operator> (const CString &Str1, const CString &Str2);friend bool operator> (const CString &Str, const char *str);friend bool operator> (const char *str, const CString &Str);// 比较两个CString对象大小(大于等于)friend bool operator>= (const CString &Str1, const CString &Str2);friend bool operator>=(const CString &Str, const char *str);friend bool operator>=(const char *str, const CString &Str);// 比较两个CString对象大小(小于)friend bool operator< (const CString &Str1, const CString &Str2);friend bool operator< (const CString &Str, const char *str);friend bool operator< (const char *str, const CString &Str);// 比较两个CString对象大小(小于等于)friend bool operator<= (const CString &Str1, const CString &Str2);friend bool operator<=(const CString &Str, const char *str);friend bool operator<=(const char *str, const CString &Str);private:// 字符串缓冲区指针(以'\0'结尾)char* m_pBuf;// 当前字符串长度(不包含终止符)int m_iLen;
};#endif // CSTRING_H
cstring.cpp
#include "CString.h"
#include <cstring>
#include <cstdarg>
#include <cassert>
#include <cctype>
#include <cstdlib>
#include <cstdio>
#include <stdexcept> // 添加这个头文件以使用 std::out_of_range// 默认构造函数
CString::CString(): m_pBuf(nullptr), m_iLen(0)
{m_pBuf = new char[1];m_pBuf[0] = '\0'; // 初始化空字符串
}// 从C风格字符串构造
CString::CString(const char* lpsz): m_pBuf(nullptr), m_iLen(0)
{if(lpsz){m_iLen = strlen(lpsz);m_pBuf = new char[m_iLen + 1];strcpy(m_pBuf, lpsz);}else{// 处理空指针输入m_pBuf = new char[1];m_pBuf[0] = '\0';}
}// 从单个字符构造
CString::CString(char ch): m_pBuf(nullptr), m_iLen(1)
{m_pBuf = new char[2];m_pBuf[0] = ch;m_pBuf[1] = '\0';
}// 拷贝构造函数
CString::CString(const CString& src): m_pBuf(nullptr), m_iLen(src.m_iLen)
{if(src.m_pBuf){m_pBuf = new char[m_iLen + 1];strcpy(m_pBuf, src.m_pBuf);}else{// 理论上不会执行,因为m_pBuf在其他构造函数中已初始化m_pBuf = new char[1];m_pBuf[0] = '\0';}
}//析构函数
CString::~CString()
{delete[] m_pBuf;m_pBuf = nullptr;
}// 获取指定索引位置的字符
char CString::GetAt(int iIndex) const
{if(iIndex >= m_iLen || iIndex < 0){// 越界时抛出异常throw std::out_of_range("Index out of range in GetAt");}return m_pBuf[iIndex];
}
//修改指定位置的字符
void CString::SetAt(int iIndex, char ch)
{if(iIndex >= m_iLen || iIndex < 0){// 越界时抛出异常throw std::out_of_range("Index out of range in SetAt");}m_pBuf[iIndex] = ch;
}
// 字符赋值运算符重载
CString& CString::operator=(char ch)
{// 释放原有内存delete[] m_pBuf;// 分配新内存(1个字符+终止符)m_iLen = 1;m_pBuf = new char[2];m_pBuf[0] = ch;m_pBuf[1] = '\0';return *this;
}
// C风格字符串赋值运算符重载
CString& CString::operator =(const char * lpsz)
{// 释放原有内存delete[] m_pBuf;if(lpsz){// 复制新字符串m_iLen = strlen(lpsz);m_pBuf = new char[m_iLen + 1];strcpy(m_pBuf, lpsz);}else{// 处理空指针(赋值为空字符串)m_iLen = 0;m_pBuf = new char[1];m_pBuf[0] = '\0';}return *this;
}
// 常量CString拷贝赋值运算符重载
CString& CString::operator=(const CString& string1)
{if(this != &string1) // 防止自赋值{// 释放原有内存delete[] m_pBuf;// 复制新字符串m_iLen = string1.m_iLen;if(m_iLen > 0){m_pBuf = new char[m_iLen + 1];strcpy(m_pBuf, string1.m_pBuf);}else{m_pBuf = new char[1];m_pBuf[0] = '\0';}}return *this;
}// 字符追加运算符重载
CString& CString::operator+=(char ch)
{// 计算新长度int newLen = m_iLen + 1;// 分配新内存char* newBuf = new char[newLen + 1];// 复制原有内容if(m_iLen > 0){strcpy(newBuf, m_pBuf);}else{newBuf[0] = '\0'; // 确保空字符串以'\0'开头}// 添加字符newBuf[m_iLen] = ch;newBuf[newLen] = '\0'; // 终止字符串// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen = newLen;return *this;
}
// C风格字符串追加运算符重载
CString& CString::operator+=(const char *str)
{if(str && *str) // 检查str不为空指针且不为空字符串{int len = strlen(str);int newLen = m_iLen + len;// 分配新内存char* newBuf = new char[newLen + 1];// 复制原有内容if(m_iLen > 0){strcpy(newBuf, m_pBuf);}else{newBuf[0] = '\0'; // 确保空字符串以'\0'开头}// 追加新字符串strcat(newBuf, str);// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen = newLen;}return *this;
}
// CString追加运算符重载
CString& CString::operator+=(const CString &Str)
{if(Str.m_iLen > 0) // 检查Str不为空字符串{int newLen = m_iLen + Str.m_iLen;// 分配新内存char* newBuf = new char[newLen + 1];// 复制原有内容if(m_iLen > 0){strcpy(newBuf, m_pBuf);}else{newBuf[0] = '\0'; // 确保空字符串以'\0'开头}// 追加新字符串strcat(newBuf, Str.m_pBuf);// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen = newLen;}return *this;
}// 格式化字符串,功能类似sprintf,返回格式化后的长度
void CString::Format(const char* pstrFormat, ...)
{if(!pstrFormat){Empty();return ;}va_list args;va_start(args, pstrFormat);// 第一次调用 vsnprintf 计算所需缓冲区大小int requiredLen = vsnprintf(nullptr, 0, pstrFormat, args);va_end(args);// 错误处理:检查 vsnprintf 的返回值if(requiredLen < 0){// 处理格式化错误(例如格式字符串包含无效格式说明符)Empty();// 可选择抛出异常或设置错误标志throw std::runtime_error("Format error: invalid format string");// 或者返回错误码(需要修改函数返回类型)return ;}// 分配内存char* newBuf = new char[requiredLen + 1];// 第二次调用 vsnprintf 格式化字符串va_start(args, pstrFormat);int actualWritten = vsnprintf(newBuf, requiredLen + 1, pstrFormat, args);va_end(args);// 再次检查返回值(理论上不会失败,但为了健壮性)if(actualWritten < 0 || actualWritten > requiredLen){delete[] newBuf;Empty();throw std::runtime_error("Format error: unexpected write failure");}// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen = requiredLen;return ;
}// 返回字符串长度(不包含终止符)
int CString::GetLength() const
{return m_iLen;
}
// 截取左边nCount个字符的子串
CString CString::Left(int nCount) const
{CString result;// 处理无效参数if(nCount <= 0){return result; // 返回空字符串}// 限制截取长度不超过原字符串长度int actualCount = (nCount > m_iLen) ? m_iLen : nCount;// 分配内存并复制子串delete[] result.m_pBuf;result.m_pBuf = new char[actualCount + 1];memcpy(result.m_pBuf, m_pBuf, actualCount);result.m_pBuf[actualCount] = '\0'; // 添加终止符result.m_iLen = actualCount;return result;
}
// 截取右边nCount个字符的子串
CString CString::Right(int nCount) const
{CString result;// 处理无效参数if(nCount <= 0){return result; // 返回空字符串}// 限制截取长度不超过原字符串长度int actualCount = (nCount > m_iLen) ? m_iLen : nCount;int startPos = m_iLen - actualCount; // 计算起始位置// 分配内存并复制子串delete[] result.m_pBuf;result.m_pBuf = new char[actualCount + 1];memcpy(result.m_pBuf, m_pBuf + startPos, actualCount);result.m_pBuf[actualCount] = '\0'; // 添加终止符result.m_iLen = actualCount;return result;
}// 从nFirst开始到末尾的子串
CString CString::Mid(int nFirst) const
{CString result;// 处理无效参数(负数或超出长度)if(nFirst < 0 || nFirst >= m_iLen){return result; // 返回空字符串}// 计算子串长度int subLen = m_iLen - nFirst;// 分配内存并复制子串delete[] result.m_pBuf;result.m_pBuf = new char[subLen + 1];memcpy(result.m_pBuf, m_pBuf + nFirst, subLen);result.m_pBuf[subLen] = '\0'; // 添加终止符result.m_iLen = subLen;return result;
}
// 从nFirst开始截取nCount个字符的子串
CString CString::Mid(int nFirst, int nCount) const
{CString result;// 处理无效参数if(nFirst < 0 || nFirst >= m_iLen || nCount <= 0){return result; // 返回空字符串}// 计算实际截取长度(避免越界)int actualCount = nCount;if(nFirst + actualCount > m_iLen){actualCount = m_iLen - nFirst;if(actualCount <= 0) return result; // 起始位置已超出末尾}// 分配内存并复制子串delete[] result.m_pBuf;result.m_pBuf = new char[actualCount + 1];memcpy(result.m_pBuf, m_pBuf + nFirst, actualCount);result.m_pBuf[actualCount] = '\0'; // 添加终止符result.m_iLen = actualCount;return result;
}
// 判断字符串是否为空
bool CString::IsEmpty() const
{return (m_iLen == 0);
}
// 清空字符串内容
void CString::Empty()
{// 释放原有内存delete[] m_pBuf;m_pBuf = nullptr;// 重置为初始状态(空字符串)m_iLen = 0;m_pBuf = new char[1];m_pBuf[0] = '\0';
}
// 获取内部缓冲区指针,用于直接操作内存
char* CString::GetBuffer(int nMinBufLength)
{if(nMinBufLength < 0 || nMinBufLength > m_iLen){return nullptr;}return m_pBuf + nMinBufLength;
}
//查找字符首次出现的位置,失败返回-1
int CString::Find(char ch) const
{for(int i = 0; i < m_iLen; i++){if(m_pBuf[i] == ch){return i;}}return -1;
}
int CString::Find(char ch, int nStart) const
{// 检查起始位置是否合法if(nStart < 0 || nStart >= m_iLen)return -1;// 从nStart位置开始查找字符chfor(int i = nStart; i < m_iLen; i++){if(m_pBuf[i] == ch)return i; // 找到字符,返回其位置}return -1; // 未找到字符
}
//查找子字符串首次出现的位置,失败返回-1
int CString::Find(const char* lpszSub) const
{if(!lpszSub || !*lpszSub) return -1; // 处理空指针或空字符串int subLen = strlen(lpszSub);if(subLen > m_iLen) return -1; // 子串长度超过原字符串const char* pos = strstr(m_pBuf, lpszSub);if(pos){return pos - m_pBuf; // 计算相对位置}return -1; // 未找到匹配子串
}//查找CString对象首次出现的位置,失败返回-1
int CString::Find(CString sSub) const
{if(sSub.IsEmpty()) return -1;return Find(sSub.GetBuffer(0));
}
//从索引uiBegin开始,返回字符串str第一次出现的位置,省略uiBegin使其为默认的0,未找到返回-1
int CString::Find(const char *str, int uiBegin) const
{if(!str || !*str) return -1; // 空字符串直接返回-1int lenStr = strlen(str);if(lenStr > m_iLen) return -1; // 查找的字符串比原字符串长// 处理无效的起始位置if(uiBegin < 0) uiBegin = 0;if(uiBegin > m_iLen - lenStr) return -1; // 剩余长度不足const char *pStart = m_pBuf + uiBegin;const char *pEnd = m_pBuf + m_iLen - lenStr + 1;// 使用strstr进行高效查找const char *pFound = strstr(pStart, str);if(pFound && pFound < pEnd){return pFound - m_pBuf; // 计算相对位置}return -1; // 未找到匹配的子字符串
}// 连接两个CString对象,返回新对象
CString operator+(const CString& string1, const CString& string2)
{int len1 = string1.GetLength();int len2 = string2.GetLength();char* pchar = new char[len1 + len2 + 1];memset(pchar, '\0', len1 + len2 + 1);// 直接访问m_pBufmemcpy(pchar, string1.m_pBuf, len1);memcpy(pchar + len1, string2.m_pBuf, len2);CString strTemp(pchar);delete[] pchar;return strTemp;
}
// CString连接字符,返回新对象
CString operator+(const CString& string1, char ch)
{int len1 = string1.GetLength();char* pchar = new char[len1 + 2];memset(pchar, '\0', len1 + 2);memcpy(pchar, string1.m_pBuf, len1);pchar[len1] = ch;CString strTemp(pchar);delete[] pchar;return strTemp;
}
// CString连接C风格字符串,返回新对象
CString operator+(const CString& string1, const char* ch)
{if(!ch){return CString("");}int len1 = string1.GetLength();int len2 = strlen(ch);char* buffer = new char[len1 + len2 + 1];memcpy(buffer, string1.m_pBuf, len1);memcpy(buffer + len1, ch, len2);buffer[len1 + len2] = '\0';CString result(buffer);delete[] buffer;return result;
}
// 实现字符串常量与CString相加
CString operator+ (const char *str, const CString &Str)
{if(str == nullptr) return CString(Str);int len = strlen(str);CString result;result.m_iLen = len + Str.m_iLen;result.m_pBuf = new char[result.m_iLen + 1];strcpy(result.m_pBuf, str);strcat(result.m_pBuf, Str.m_pBuf);return result;
}// 实现字符与CString相加
CString operator+ (char ch, const CString &Str)
{CString result;result.m_iLen = 1 + Str.m_iLen;result.m_pBuf = new char[result.m_iLen + 1];result.m_pBuf[0] = ch;strcpy(result.m_pBuf + 1, Str.m_pBuf);result.m_pBuf[result.m_iLen] = '\0';return result;
}
// 判断CString与C风格字符串是否不相等
bool operator!=(const CString& string1, const char* ch)
{// 处理ch为nullptr的情况if(!ch){return !string1.IsEmpty(); // string1非空时返回true}// 使用strcmp比较内容return strcmp(string1.m_pBuf, ch) != 0;
}
// 判断CString与C风格字符串是否相等
bool operator==(const CString& string1, const char* ch)
{// 处理ch为nullptr的情况if(!ch){return string1.IsEmpty(); // string1为空时返回true}// 使用strcmp比较内容return strcmp(string1.m_pBuf, ch) == 0;
}
// 判断两个CString对象是否相等
bool operator==(const CString& string1, const CString& string2)
{if(string1.GetLength() != string2.GetLength()){return false;}return strcmp(string1.m_pBuf, string2.m_pBuf) == 0;
}
bool operator==(const char *str, const CString &Str)
{// 处理str为nullptr的情况if(str == nullptr){return Str.m_iLen == 0; // nullptr只能与空字符串相等}// 比较长度是否一致if(strlen(str) != Str.m_iLen){return false;}// 逐个字符比较内容return strncmp(str, Str.m_pBuf, Str.m_iLen) == 0;
}// 判断两个CString对象是否不相等
bool operator!=(const CString& string1, const CString& string2)
{// 长度不同则直接不相等if(string1.GetLength() != string2.GetLength()){return true;}// 使用strcmp比较内容return strcmp(string1.m_pBuf, string2.m_pBuf) != 0;
}
//将字符串转化为一个大写的字符串
void CString::MakeUpper()
{for(int i = 0; i < m_iLen; i++){if(m_pBuf[i] >= 'a' && m_pBuf[i] <= 'z'){m_pBuf[i] -= 32; // 转换为大写(ASCII码差值为32)}}
}
//将字符串转化为一个小写的字符串
void CString::MakeLower()
{for(int i = 0; i < m_iLen; i++){if(m_pBuf[i] >= 'A' && m_pBuf[i] <= 'Z'){m_pBuf[i] += 32; // 转换为小写(ASCII码差值为32)}}
}
void CString::TrimLeft()
{int i = 0;// 查找第一个非空白字符的位置while(m_pBuf[i] != '\0' && isspace(m_pBuf[i]))i++;// 如果存在前导空白字符,则移动字符串if(i > 0){memmove(m_pBuf, m_pBuf + i, m_iLen - i + 1); // 包含终止符'\0'm_iLen -= i;}
}void CString::TrimRight()
{int i = m_iLen - 1;// 查找最后一个非空白字符的位置while(i >= 0 && isspace(m_pBuf[i]))i--;// 如果存在尾部空白字符,则设置新的字符串长度并添加终止符if(i < m_iLen - 1){m_iLen = i + 1;m_pBuf[m_iLen] = '\0';}
}
//去掉左右两边的空格
void CString::Trim()
{TrimLeft();TrimRight();
}// 比较两个CString对象大小(大于)
bool operator>(const CString &Str1, const CString &Str2)
{return strcmp(Str1.m_pBuf, Str2.m_pBuf) > 0;
}
// 比较两个CString对象大小(小于)
bool operator<(const CString &Str1, const CString &Str2)
{return strcmp(Str1.m_pBuf, Str2.m_pBuf) < 0;
}
// 比较两个CString对象大小(大于等于)
bool operator>=(const CString &Str1, const CString &Str2)
{return strcmp(Str1.m_pBuf, Str2.m_pBuf) >= 0;
}
// 比较两个CString对象大小(小于等于)
bool operator<=(const CString &Str1, const CString &Str2)
{return strcmp(Str1.m_pBuf, Str2.m_pBuf) <= 0;
}
// 下标运算符重载
char& CString::operator[](int iIndex)
{if(iIndex >= m_iLen || iIndex < 0){// 越界时抛出异常throw std::out_of_range("Index out of range in operator[]");}return m_pBuf[iIndex];
}// 常量版本的下标运算符重载
const char& CString::operator[](int iIndex) const
{if(iIndex >= m_iLen || iIndex < 0){// 越界时抛出异常throw std::out_of_range("Index out of range in operator[] const");}return m_pBuf[iIndex];
}
//反转
CString& CString::MakeReverse()
{int left = 0;int right = m_iLen - 1;while(left < right){// 交换左右字符char temp = m_pBuf[left];m_pBuf[left] = m_pBuf[right];m_pBuf[right] = temp;left++;right--;}return *this; // 返回自身引用,支持链式调用
}
// 查找字符最后一次出现的位置,失败返回-1
int CString::ReverseFind(char ch) const
{for(int i = m_iLen - 1; i >= 0; i--){if(m_pBuf[i] == ch){return i;}}return -1; // 未找到
}// 替换所有指定字符,返回替换次数
int CString::Replace(char chOld, char chNew)
{int count = 0;for(int i = 0; i < m_iLen; i++){if(m_pBuf[i] == chOld){m_pBuf[i] = chNew;count++;}}return count;
}// 替换所有子字符串,返回替换次数
int CString::Replace(const char* lpszOld, const char* lpszNew)
{if(!lpszOld || !*lpszOld){return 0;}if(!lpszNew){throw std::invalid_argument("lpszNew is nullptr");}int lenOld = strlen(lpszOld);int lenNew = strlen(lpszNew);int count = 0;// 计算替换后的新长度int newLen = m_iLen;const char* p = m_pBuf;while((p = strstr(p, lpszOld))){newLen += lenNew - lenOld;p += lenOld;count++;}if(count == 0) return 0; // 没有找到匹配项// 分配新缓冲区char* newBuf = new char[newLen + 1];char* dest = newBuf;const char* src = m_pBuf;// 执行替换while(*src){if(strncmp(src, lpszOld, lenOld) == 0){memcpy(dest, lpszNew, lenNew);dest += lenNew;src += lenOld;}else{*dest++ = *src++;}}*dest = '\0'; // 终止字符串// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen = newLen;return count;
}// 删除所有指定字符,返回删除次数
int CString::Remove(char ch)
{int count = 0;char* dest = m_pBuf;for(int i = 0; i < m_iLen; i++){if(m_pBuf[i] != ch){*dest++ = m_pBuf[i];}else{count++;}}*dest = '\0'; // 终止字符串m_iLen -= count;return count;
}// 在指定位置插入字符,返回新字符串长度
int CString::Insert(int nIndex, char ch)
{if(nIndex < 0){throw std::out_of_range("Insert position is negative");}if(nIndex > m_iLen){throw std::out_of_range("Insert position is greater than the current length");}// 分配新缓冲区char* newBuf = new char[m_iLen + 2]; // +1 为插入的字符,+1 为终止符// 复制前半部分memcpy(newBuf, m_pBuf, nIndex);// 插入字符newBuf[nIndex] = ch;// 复制后半部分memcpy(newBuf + nIndex + 1, m_pBuf + nIndex, m_iLen - nIndex);// 终止字符串newBuf[m_iLen + 1] = '\0';// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen++;return m_iLen;
}// 在指定位置插入字符串,返回新字符串长度
int CString::Insert(int nIndex, char* lpsz)
{if(!lpsz){throw std::invalid_argument("lpsz is nullptr");}if(nIndex < 0){throw std::out_of_range("Insert position is negative");}if(nIndex > m_iLen){throw std::out_of_range("Insert position is greater than the current length");}int lenInsert = strlen(lpsz);// 分配新缓冲区char* newBuf = new char[m_iLen + lenInsert + 1];// 复制前半部分memcpy(newBuf, m_pBuf, nIndex);// 插入字符串memcpy(newBuf + nIndex, lpsz, lenInsert);// 复制后半部分memcpy(newBuf + nIndex + lenInsert, m_pBuf + nIndex, m_iLen - nIndex);// 终止字符串newBuf[m_iLen + lenInsert] = '\0';// 更新对象状态delete[] m_pBuf;m_pBuf = newBuf;m_iLen += lenInsert;return m_iLen;
}// 删除指定位置开始的nCount个字符,返回剩余字符串长度
int CString::Delete(int nIndex, int nCount)
{if(nIndex < 0 || nIndex >= m_iLen){throw std::out_of_range("Delete position is out of range");}if(nCount <= 0){throw std::invalid_argument("nCount should be greater than 0");}// 确保不越界if(nIndex + nCount > m_iLen){nCount = m_iLen - nIndex;}// 移动后面的字符覆盖删除部分memmove(m_pBuf + nIndex, m_pBuf + nIndex + nCount, m_iLen - (nIndex + nCount) + 1);// 更新长度m_iLen -= nCount;return m_iLen;
}
//在当前字符串中查找 lpszCharSet 中任意一个字符首次出现的位置
int CString::FindOneOf(const char* lpszCharSet) const
{if(!lpszCharSet || !*lpszCharSet)return -1; // 空字符集for(int i = 0; i < m_iLen; i++){for(const char* p = lpszCharSet; *p; p++){if(m_pBuf[i] == *p){return i; // 找到第一个匹配字符的位置}}}return -1; // 未找到任何匹配字符
}
bool operator!=(const char *str, const CString &Str)
{if(str == nullptr)return Str.m_iLen != 0;return strlen(str) != Str.m_iLen || strncmp(str, Str.m_pBuf, Str.m_iLen) != 0;
}bool operator< (const CString &Str, const char *str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return false; // 非空字符串不小于nullptr}// 使用strcmp比较两个字符串return strcmp(Str.m_pBuf, str) < 0;
}bool operator< (const char *str, const CString &Str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return Str.m_iLen > 0; // nullptr小于任何非空字符串}// 使用strcmp比较两个字符串return strcmp(str, Str.m_pBuf) < 0;
}bool operator> (const CString &Str, const char *str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return Str.m_iLen > 0; // 非空字符串大于nullptr}// 使用strcmp比较两个字符串return strcmp(Str.m_pBuf, str) > 0;
}bool operator<=(const CString &Str, const char *str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return Str.m_iLen == 0; // 仅空字符串<=nullptr}// 使用strcmp比较两个字符串return strcmp(Str.m_pBuf, str) <= 0;
}bool operator<=(const char *str, const CString &Str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return true; // nullptr小于等于任何字符串}// 使用strcmp比较两个字符串return strcmp(str, Str.m_pBuf) <= 0;
}bool operator> (const char *str, const CString &Str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return false; // nullptr不大于任何字符串}// 使用strcmp比较两个字符串return strcmp(str, Str.m_pBuf) > 0;
}// 比较CString对象是否大于等于字符串常量(按字典序)bool operator>=(const CString &Str, const char *str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return true; // 任何字符串都大于等于nullptr}// 使用strcmp比较两个字符串return strcmp(Str.m_pBuf, str) >= 0;
}// 比较字符串常量是否大于等于CString对象(按字典序)bool operator>=(const char *str, const CString &Str)
{// 处理str为nullptr的情况:nullptr视为最小字符串if(str == nullptr){return Str.m_iLen == 0; // nullptr仅大于等于空字符串}// 使用strcmp比较两个字符串return strcmp(str, Str.m_pBuf) >= 0;
}int CString::Compare(const char *str) const
{if(str == nullptr){return m_iLen > 0 ? 1 : 0; // nullptr视为空字符串}return strcmp(m_pBuf, str);
}int CString::Compare(const CString &Str) const
{return strcmp(m_pBuf, Str.m_pBuf);
}int CString::CompareNoCase(const char *str) const
{if(str == nullptr){return m_iLen > 0 ? 1 : 0; // nullptr视为空字符串}return strcasecmp(m_pBuf, str); // Windows: _stricmp, Linux: strcasecmp
}int CString::CompareNoCase(const CString &Str) const
{return strcasecmp(m_pBuf, Str.m_pBuf); // Windows: _stricmp, Linux: strcasecmp
}
测试代码
void testConstructors()
{// 默认构造函数CString s1;assert(s1.GetLength() == 0);assert(s1.IsEmpty());// 从C风格字符串构造CString s2("Hello");assert(s2.GetLength() == 5);assert(strcmp(s2.GetBuffer(0), "Hello") == 0);// 从单个字符构造CString s3('A');assert(s3.GetLength() == 1);assert(s3.GetAt(0) == 'A');// 复制构造函数CString s4(s2);assert(s4.GetLength() == 5);assert(strcmp(s4.GetBuffer(0), "Hello") == 0);
}void testGetLengthAndIsEmpty()
{CString s;assert(s.GetLength() == 0);assert(s.IsEmpty());s = "Test";assert(s.GetLength() == 4);assert(!s.IsEmpty());s.Empty();assert(s.GetLength() == 0);assert(s.IsEmpty());
}void testElementAccess()
{CString s("abc");assert(s.GetAt(0) == 'a');assert(s[1] == 'b');s.SetAt(1, 'X');assert(s[1] == 'X');const CString cs("test");assert(cs[2] == 's');
}void testFindAndReverseFind()
{CString s("Hello World");assert(s.Find('o') == 4);assert(s.Find('o', 5) == 7);assert(s.Find("World") == 6);assert(s.Find("xyz") == -1);assert(s.ReverseFind('l') == 9);assert(s.FindOneOf("aeiou") == 1);assert(s.Find('d',10)==10);
}void testModifyString()
{CString s("Hello");// Replaceassert(s.Replace('l', 'L') == 2);assert(strcmp(s.GetBuffer(0), "HeLLo") == 0);// Removeassert(s.Remove('L') == 2);assert(strcmp(s.GetBuffer(0), "Heo") == 0);// Insertassert(s.Insert(2, 'l') == 4);assert(strcmp(s.GetBuffer(0), "Helo") == 0);assert(s.Insert(3, "lo") == 6);assert(strcmp(s.GetBuffer(0), "Helloo") == 0);// Deleteassert(s.Delete(2, 3) == 3);assert(strcmp(s.GetBuffer(0), "Heo") == 0);
}void testAssignmentOperators()
{CString s;// 字符赋值s = 'A';assert(s.GetLength() == 1);assert(s[0] == 'A');// C风格字符串赋值s = "Test";assert(s.GetLength() == 4);assert(strcmp(s.GetBuffer(0), "Test") == 0);// CString赋值CString s2("Copy");s = s2;assert(s.GetLength() == 4);assert(strcmp(s.GetBuffer(0), "Copy") == 0);
}void testAppendOperators()
{CString s("Hello");// 追加字符s += ' ';assert(strcmp(s.GetBuffer(0), "Hello ") == 0);// 追加C风格字符串s += "World";assert(strcmp(s.GetBuffer(0), "Hello World") == 0);// 追加CStringCString suffix("!");s += suffix;assert(strcmp(s.GetBuffer(0), "Hello World!") == 0);
}void testFormat()
{CString s;s.Format("%d + %d = %d", 2, 3, 5);assert(strcmp(s.GetBuffer(0), "2 + 3 = 5") == 0);float value=1.234;s.Format("%.2f",value);assert(strcmp(s.GetBuffer(0), "1.23") == 0);s.Format("%02x",55);printf("s=%s\n",s.GetBuffer(0));assert(strcmp(s.GetBuffer(0), "37") == 0);
}void testSubstring()
{CString s("HelloWorld");assert(strcmp(s.Left(5).GetBuffer(0), "Hello") == 0);assert(strcmp(s.Right(5).GetBuffer(0), "World") == 0);assert(strcmp(s.Mid(5).GetBuffer(0), "World") == 0);assert(strcmp(s.Mid(1, 3).GetBuffer(0), "ell") == 0);
}void testCaseConversion()
{CString s("Hello");s.MakeUpper();assert(strcmp(s.GetBuffer(0), "HELLO") == 0);s.MakeLower();assert(strcmp(s.GetBuffer(0), "hello") == 0);
}void testTrim()
{CString s(" \tHello World! \n");CString s2(" ");s2.Trim();assert(s2.IsEmpty());s.TrimLeft();assert(strcmp(s.GetBuffer(0), "Hello World! \n") == 0);s.TrimRight();assert(strcmp(s.GetBuffer(0), "Hello World!") == 0);s = " \t Test \t ";s.Trim();assert(strcmp(s.GetBuffer(0), "Test") == 0);
}void testReverse()
{CString s("abc");s.MakeReverse();assert(strcmp(s.GetBuffer(0), "cba") == 0);
}void testConcatenation()
{CString s1("Hello");CString s2(" World");CString s3 = s1 + s2;assert(strcmp(s3.GetBuffer(0), "Hello World") == 0);CString s4 = s1 + '!';assert(strcmp(s4.GetBuffer(0), "Hello!") == 0);CString s5 = "Hi" + s1;assert(strcmp(s5.GetBuffer(0), "HiHello") == 0);CString s6 = 'A' + s1;assert(strcmp(s6.GetBuffer(0), "AHello") == 0);
}void testComparisonOperators()
{CString s1("abc");CString s2("abc");CString s3("abd");// ==assert(s1 == s2);assert(s1 == "abc");assert("abc" == s1);// !=assert(s1 != s3);assert(s1 != "abd");assert("abd" != s1);// >assert(s3 > s1);assert(s3 > "abc");assert("abd" > s1);// >=assert(s1 >= s2);assert(s3 >= s1);assert(s1 >= "abc");assert("abd" >= s1);// <assert(s1 < s3);assert(s1 < "abd");assert("abc" < s3);// <=assert(s1 <= s2);assert(s1 <= s3);assert(s1 <= "abd");assert("abc" <= s3);
}
void testCompare()
{CString s1("Hello");CString s2("hello");int r1 = s1.Compare("Hello"); // 0assert(r1==0);int r2 = s1.Compare("World"); // <0assert(r2<0);int r3 = s1.CompareNoCase("HELLO"); // 0assert(r3==0);int r4 = s1.Compare(s2); // <0 (大小写敏感)assert(r4<0);int r5 = s1.CompareNoCase(s2); // 0 (大小写不敏感)assert(r5==0);
}