自定义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);
}
相关文章:
自定义CString类与MFC CString类接口对比
接口对比表格 功能分类 你的 CString 接口 MFC CString 接口(ANSI) 一致性 差异说明 构造函数 CString() CString(const char*) CString(char) CString(const CString&) CString() CString(LPCSTR) CString(TCHAR) CString(const CString&…...

华为OD机试真题——考勤信息(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…...

Go语言测试用例的执行与分析
在软件开发过程中,测试用例是确保代码质量的关键环节。Go语言作为一种现代的编程语言,它内置了强大的测试框架,可以帮助开发者轻松编写和执行测试用例。本文将介绍如何在 Go 语言中编写、执行测试用例,并对测试结果进行分析。 ## …...
vue3 vite 路由
如路由是这种格式 http://localhost:7058/admin/product/brand路由配置如下 import { createRouter, createWebHistory } from vue-router import HomeView from ../views/HomeView.vue import NProgress from nprogress; import nprogress/nprogress.css; import {errorRour…...

MyBatis:动态SQL
文章目录 动态SQLif标签trim标签where标签set标签foreach标签include标签和sql标签 Mybatis动态SQL的官方文档: https://mybatis.net.cn/dynamic-sql.html 动态SQL 动态SQL是 MyBatis的强大特性之一,如果是使用JDBC根据不同条件拼接sql很麻烦,例如拼接…...

游戏引擎学习第280天:精简化的流式实体sim
回顾并为今天的内容做铺垫 今天的任务是让之前关于实体存储方式的改动真正运行起来。我们现在希望让实体系统变得更加真实和实用,能够支撑我们游戏实际所需的功能。这就要求我们对它进行更合理的实现和调试。 昨天我们基本让代码编译通过了,但实际上还…...
femap许可与多用户共享
随着电磁仿真技术的发展,Femap作为一款领先的工具,在多个领域中发挥着不可替代的作用。然而,对于许多团队和企业来说,如何高效、经济地管理和使用Femap许可证成为了一个亟待解决的问题。本文将探讨Femap许可与多用户共享的概念、优…...

王树森推荐系统公开课 排序03:预估分数融合
融合预估分数 p c l i c k ⋅ p l i k e p_{click} \cdot p_{like} pclick⋅plike 有实际意义,等于在曝光中点赞的概率。 p c l i c k ⋅ p c o l l e c t p_{click} \cdot p_{collect} pclick⋅pcollect 同理。 按多种排名做 ensemble sort。 某电商的融…...

网络I/O学习-poll(三)
一、为什么要用Poll 由于select参数太多,较于复杂,调用起来较为麻烦;poll对其进行了优化 二、poll机制 poll也是一个系统调用,每次调用都会将所有客户端的fd拷贝到内核空间,然后进行轮询,判断IO是否就绪…...

k8s(12) — 版本控制和滚动更新(金丝雀部署理念)
金丝雀部署简介: 1、基本概念 金丝雀部署是一种软件开发中的渐进式发布策略,其核心思想是通过将新版本应用逐步发布给一小部分用户(即 “金丝雀” 用户),在真实环境中验证功能稳定性和性能表现,再逐步扩大发…...
【git config --global alias | Git分支操作效率提升实践指南】
git config --global alias | Git分支操作效率提升实践指南 背景与痛点分析 在现代软件开发团队中,Git分支管理是日常工作的重要组成部分。特别是在规范的开发流程中,我们经常会遇到类似 feature/user-management、bugfix/login-issue 或 per/cny/dev …...
chrome源码中WeakPtr 跨线程使用详解:原理、风险与最佳实践
base::WeakPtr 在 Chromium 中 不能安全地跨线程使用。这是一个很关键的点,下面详细解释原因及正确用法。 🔍原理与使用 ✅ 先说答案: base::WeakPtr 本质上是**线程绑定(thread-affine)**的。不能在多个线程之间创建…...
【Go】从0开始学习Go
文章目录 从0开始学习Go0 与C对比1 代码框架1.1 helloworld式代码示例1.2 主体代码元素(核心三部分)1.3 其他 2 与C/C区别3 有用的小工具4 注意事项 从0开始学习Go 0 与C对比 特性CGo编译型语言需要编译为机器码直接编译为二进制可执行文件静态类型类型…...
Windows 安装显卡驱动
1.第一步:打开Nvidia 官网驱动下载页面 2.第二步:选择相关信息, 玩游戏选择,GeForce Game Ready ,创意设计、摄影直播 选择 NVIDIA Studio 驱动程序 (NVIDIA Studio Driver - WHQL.) 2.第三步࿱…...
模块与包的导入
一、导入官方库 我们复盘下学习python的逻辑,所谓学习python就是学习python常见的基础语法学习你所处理任务需要用到的第三方库 类别典型库解决的问题学习门槛基础工具os、sys、json操作系统交互、序列化数据(如读写 JSON 文件)低科学计算n…...

Google设置app-ads.txt
问题: 应用上架后admob后台显示应用广告投放量受限,需要设置app-ads.txt才行。 如何解决: 官方教程: 看了下感觉不难,创建一个txt,将第二条的代码复制进行就得到app-ads.txt了。 然后就是要把这个txt放到哪才可以…...

docker安装rockerMQ
参考Docker部署RocketMQ5.x (单机部署配置参数详解不使用docker-compose直接部署)_rocketmq不推荐用docker部署-CSDN博客 镜像拉取 镜像地址: https://hub.docker.com/r/apache/rocketmq/tags 我在部署的时候最新发行版是5.1.0可以根据需求自行选择一个5.x的版本&a…...

交叉引用、多个参考文献插入、跨文献插入word/wps中之【插入[1-3]、连续文献】
我们在写论文时,需要插入大量参考文献。 有时,一句话需要引用多个文献,如:[1-3]或者[1,3,4]这种形式多个文献插入、跨文献插入。 在上一篇文章中,我们提到可以直接打“-”或者“,”,但是word导出…...

PLC双人舞:profinet转ethernet ip网关奏响施耐德与AB的协奏曲
PLC双人舞:ethernet ip转profinet网关奏响施耐德与AB的协奏曲 案例分析:施耐德PLC与AB PLC的互联互通 在现代工业自动化中,设备之间的互联互通至关重要。本案例旨在展示如何通过北京倍讯科技的EtherNet/IP转Modbus网关,将施耐德P…...
Image and depth from a conventional camera with a coded aperture论文阅读
Image and depth from a conventional camera with a coded aperture 1. 研究目标与实际意义1.1 研究目标1.2 实际问题与产业意义2. 创新方法:编码光圈设计与统计模型2.1 核心思路2.2 关键公式与模型架构2.2.1 图像形成模型2.2.2 深度可区分性准则2.2.3 统计模型与优化框架2.2…...

缺乏团队建设活动,如何增强凝聚力?
当一个团队缺乏系统性的建设活动时,成员之间容易产生疏离感、误解与信任缺失,最终影响整体执行力和目标达成。要有效增强团队凝聚力,应从设计高参与感的团队活动、结合业务与人文目标、营造持续共创的文化机制、推动跨层级协作互动等层面着手…...
特征筛选方法总结
非模型方法 一.FILTER过滤法: 1.缺失值比例(80%以上缺失则删除)/方差 注意: 连续变量只删方差为0的,因为变量取值范围会影响方差大小。 离散类的看各类取值占比,如果是三分类变量可以视作连续变量。 函数:V…...

力扣HOT100之二叉树:230. 二叉搜索树中第 K 小的元素
这道题直接用最笨的办法来做的,用递归来做,我们定义一个全局变量vector<int> element,然后使用中序遍历,每当碰到一个非空节点就将其加入到向量中,这样依赖当向量中的元素小于k时,就返回0,…...
pinia.defineStore is not a function
错误信息表明 pinia.defineStore 不是一个函数,这通常意味着 pinia 没有被正确导入或初始化。 解决方案 检查 Pinia 的导入 确保你从 pinia 中正确导入了 defineStore。正确的导入方式应该是: javascript import { defineStore } from ‘pinia’; 如果你使用的是 createPin…...
入职软件开发与实施工程师了后........
时隔几个月没有创作的我又回来了,这几个月很忙,我一直在找工作,在自考(顺便还处理了一下分手的事),到处奔波,心力交瘁。可能我骨子里比较傲吧。我不愿意着急谋生,做我不愿意做的普通…...
PCL点云库点云数据处理入门系列教材目录(2025年5月更新....)
PCL点云库点云数据处理入门系列教材目录 基础阶段 第 1 讲:PCL库简介和安装(Win10/11VS2019PCL 1.12.0)第 2 讲:PCL库中点云基本知识和数据类型结构第 3 讲:PCL库中点云数据格式PCD和PLY及其输入输出(IO&…...

Linux面试题集合(5)
把文件1的内容追加到文件2 cat 文件1>>文件2 把文件1和文件2合并成文件3 cat 文件1 文件2>文件3 使用less查看文件时,搜寻ab字符 /ab 用more和less如何查看文件 more: CtrlF -- 向下滚动一屏 CtrlB -- 返回上一屏 f -- 向下翻屏 b -- 向上翻屏 …...

python动漫论坛管理系统
目录 技术栈介绍具体实现截图系统设计研究方法:设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理,难度适中…...
【ubuntu24.04】pycharm 死机结束进程
windows 远程pycharm到ubuntu执行程序 pycharm 在调试过程中,内存耗尽,然后死机了 pycharm 进程 (base) rootk8s-master-pfsrv:/home/zhangbin/下载# ps -ef | grep pycharm root 121245 3230568 0 5月14 pts/8 00:00:00 /bin/bash --rcfile …...
Java 中Supplier延迟生成值的原因
在编程中,延迟生成值(Lazy Value Generation) 是指将值的计算或生成过程推迟到真正需要使用该值时才执行。这一机制的核心是避免不必要的计算,提升程序的性能和资源利用率。结合 Supplier 和 Optional 的使用场景,我们…...