自定义C++ String类赋值时旧内存检查未执行问题排查
问题根源:混淆了构造函数调用与赋值操作
嘿,你忽略了一个关键的C++基础概念:构造函数只会在对象初始化的时候被调用,而msg = "Hello";是对一个已经存在的对象执行赋值操作,根本不会触发你写的那个带const char*参数的构造函数!
具体发生了什么?
String msg;阶段:这是对象初始化,正确调用了默认构造函数,此时msg.str指向了你分配的1字节内存,确实是非NULL的。msg = "Hello";阶段:这是赋值操作,不是初始化。因为msg已经存在,C++会做两件事:- 首先,把字符串字面量
"Hello"隐式转换成一个临时的String对象(这时候会调用你写的带const char*的构造函数,但这个临时对象的str初始是NULL,所以它的if (str != NULL)分支也不会执行); - 然后,调用编译器自动生成的默认拷贝赋值运算符,把临时对象的
len和str直接拷贝给msg。
- 首先,把字符串字面量
这就导致:你原本期望在赋值时释放msg旧内存的逻辑完全没被执行,反而因为默认赋值的浅拷贝,最后还会导致内存泄漏(msg原来的内存没被释放,现在又指向了临时对象分配的内存,临时对象销毁后还可能出现野指针问题)。
你的构造函数还有额外问题
顺便提一句:你写的带const char*参数的构造函数里,if (str != NULL)的判断完全没用!因为构造函数是用来创建新对象的,类成员的默认初始化char *str = NULL;会在构造函数体执行前完成,所以这个构造函数里的str一开始就是NULL,这个分支永远不会进入。而且构造新对象时根本不需要释放内存,因为还没分配过,这个判断属于画蛇添足。
如何修正?
你需要自己重载赋值运算符,把你期望的内存释放/重新分配逻辑放在这里:
class String { public: int len; char *str = NULL; // Default constructor String() { len = 0; str = new char[1]; str[0] = '\0'; printf("New memory: %p\n", str); } // Constructor from const char* String(const char *ch) { len = strlen(ch); // 这里不需要检查str,因为是新对象,str初始为NULL str = new char[len + 1]; printf("New memory: %p\n", str); strcpy(str, ch); // 用strcpy更简洁,和你的循环效果一致 } // 重载赋值运算符,处理const char*的赋值 String& operator=(const char* ch) { // 先释放旧内存 if (str != NULL) { printf("Old memory: %p\n", str); delete[] str; // 注意用delete[],因为是new[]分配的! printf("Deleted...\n"); } // 重新分配内存并拷贝内容 len = strlen(ch); str = new char[len + 1]; printf("New memory: %p\n", str); strcpy(str, ch); return *this; } // 别忘了添加析构函数,防止内存泄漏! ~String() { if (str != NULL) { delete[] str; } } };
关键要点总结
- 区分初始化(
String msg("Hello");或String msg = "Hello";)和赋值(msg = "Hello";):前者调用构造函数,后者调用赋值运算符。 - 动态分配内存的类,必须手动重载赋值运算符(以及拷贝构造函数,不然浅拷贝会出问题),还要写析构函数释放内存——这就是C++的三大法则(Rule of Three)。
- 用
new[]分配的内存,必须用delete[]释放,不能用delete,否则会有未定义行为。
内容的提问来源于stack exchange,提问作者Next-93
相关产品推荐
相关产品推荐

