C++自定义字符串类char*拷贝值为空、双重释放问题排查
问题核心原因
你遇到的重复释放、垃圾值、空值问题,本质是三个实现漏洞:
- 浅拷贝导致多个对象的
Input指针指向同一块堆内存,析构时多次触发free - 深拷贝逻辑没有为字符串结束符
\0预留空间,或拷贝完成后未手动追加结束符,C风格字符串读取时会越界访问内存,出现垃圾值 - 混淆了拷贝构造和赋值运算符的调用场景:
CustomStringClass Obj2 = xxx是对象初始化语句,调用的是拷贝构造函数,不是赋值运算符,你之前只重载赋值运算符无法覆盖这个场景
另外代码里存在拼写错误:Substirng应为Substring。
修复方案
不需要引入额外库函数,也不需要修改析构函数的free逻辑,按以下规则补全实现即可:
- 所有申请堆内存存储字符串的场景,内存大小 = 字符串实际长度 + 1,多出来的1字节专门存结束符
\0 - 拷贝构造函数必须为新对象独立申请堆内存,逐字节复制源对象的字符串内容,最后手动追加结束符,禁止直接复制指针地址
- 重载赋值运算符时先判断自赋值场景,释放当前对象原有内存后再申请新空间拷贝内容,避免内存泄漏和野指针
- 所有字符串长度计算、内容拷贝逻辑通过手动遍历字符实现,不依赖库函数
完整修复后代码
class CustomStringClass { private: char* Input; // 私有辅助函数,计算C风格字符串长度,不依赖第三方库函数 int strLen(const char* s) { int len = 0; while(s[len] != '\0') len++; return len; } public: // 普通构造函数 CustomStringClass(const char* inputStr) { int len = strLen(inputStr); // 申请内存:长度+1用于存储结束符 Input = (char*)malloc(len + 1); // 逐字节拷贝内容 for(int i = 0; i < len; i++) { Input[i] = inputStr[i]; } // 手动追加字符串结束符 Input[len] = '\0'; } // 深拷贝构造函数 CustomStringClass(const CustomStringClass& other) { int len = strLen(other.Input); Input = (char*)malloc(len + 1); for(int i = 0; i < len; i++) { Input[i] = other.Input[i]; } Input[len] = '\0'; } // 深拷贝赋值运算符重载 CustomStringClass& operator=(const CustomStringClass& other) { // 自赋值场景直接返回,避免释放自身内存后拷贝失败 if(this == &other) { return *this; } // 释放当前对象原有内存,避免内存泄漏 free(Input); int len = strLen(other.Input); Input = (char*)malloc(len + 1); for(int i = 0; i < len; i++) { Input[i] = other.Input[i]; } Input[len] = '\0'; return *this; } CustomStringClass Substring(int Start, int End) { // 计算子串实际长度 int subLen = End - Start + 1; // 申请临时缓冲区存储截取的子串 char* subBuf = (char*)malloc(subLen + 1); for(int i = 0; i < subLen; i++) { subBuf[i] = Input[Start + i]; } subBuf[subLen] = '\0'; CustomStringClass temp(subBuf); // 释放临时缓冲区,避免泄漏 free(subBuf); return temp; } // 测试用接口,返回内部存储的字符串 const char* c_str() { return Input; } ~CustomStringClass(){ free(this->Input); } }; int main() { CustomStringClass Obj1("Hello"); CustomStringClass Obj2 = Obj1.Substring(2,3); // 此时Obj2.c_str()输出结果为"ll",无重复释放、无内存泄漏、无垃圾值 return 0; }
验证说明
- 所有对象的
Input指针都指向独立的堆内存,析构时各自释放自身申请的内存空间,不会出现重复释放问题 - 所有字符串都手动追加了
\0结束符,不会出现越界读取导致的垃圾值、空值问题 - 没有引入额外头文件和库函数,所有字符串操作均通过手动遍历实现
- 析构函数的
free逻辑完全保留,未做注释或修改
内容的提问来源于stack exchange,提问作者Fayyaaz
相关产品推荐
相关产品推荐

