C++自定义String类display方法中cout仅输出首字符后停止的问题排查
嘿,我仔细看了你的代码,发现问题的核心是悬空指针导致的未定义行为,这也是C++新手很容易踩的坑!咱们一步步拆解问题:
问题根源:构造函数的致命错误
你的String构造函数里,a = &b[0];只是把指针直接指向了传入的char数组的首地址,完全没有自己分配内存去复制字符串内容。这会引发两个严重问题:
- 当传入的是栈上的局部数组(比如
main里的input1/input2,或者operator+里的temp),这些数组在所在函数执行完毕后就会被系统销毁,你的String对象里的a指针就变成了悬空指针——指向的内存已经不属于你的程序了,访问它的行为是完全不可预测的。 - 比如你在
operator+里创建的temp是栈上的局部数组,当operator+执行完返回时,temp的内存就被释放了,这时候返回的String对象的a指针指向的就是一块无效内存。当你调用display时,访问这块内存就会出现各种奇怪的现象,比如只输出首字符、乱码甚至程序崩溃。
你说在构造函数里用循环输出能正常显示,那是因为构造函数执行时,传入的char数组还在栈上(比如main里的input1还没被销毁),但operator+返回的String对象就没这么幸运了——它的a指向的temp已经被销毁了。
修正方案:修复内存管理问题
要解决这个问题,我们需要让String类真正拥有自己的内存,而不是依赖外部数组。
1. 重写构造函数,手动分配内存并复制内容
把原来的构造函数改成这样,根据传入字符串的实际长度分配内存,而不是固定101:
#include <cstring> // 需要用到strlen和strcpy class String { public: char *a; int length; // 构造函数:接收const char*,避免修改原字符串 String(const char* b) { length = strlen(b); // 获取字符串实际长度(不含'\0') a = new char[length + 1]; // 给'\0'预留位置 strcpy(a, b); // 复制字符串内容到自己的内存 } String operator+(const String &other); void display(); };
2. 添加析构函数,避免内存泄漏
因为我们用new分配了内存,必须在对象销毁时释放,否则会造成内存泄漏:
~String() { delete[] a; // 释放数组内存,注意用delete[]而不是delete }
3. 修正operator+函数,避免返回指向栈内存的对象
现在构造函数会自己复制内容,我们可以在operator+里先分配足够的内存,拼接两个字符串后再返回新的String对象:
#include <iostream> #include "stringConcat.h" #include <cstring> using namespace std; String String::operator+(const String &other) { // 计算总长度:原字符串长度 + 另一个字符串长度 + 1(空格) + 1('\0') int totalLen = length + other.length + 2; char* temp = new char[totalLen]; // 复制第一个字符串 strcpy(temp, a); // 添加空格 strcat(temp, " "); // 拼接第二个字符串 strcat(temp, other.a); String result(temp); delete[] temp; // 构造函数已经复制了内容,这里释放临时内存 return result; } void String::display() { cout << a << endl; // 直接输出字符串即可,不用手动循环,'\0'会自动终止输出 }
4. 补充拷贝构造函数和赋值运算符重载(必加!)
C++默认的拷贝构造函数和赋值运算符是浅拷贝——只会复制指针,导致多个String对象指向同一块内存,当其中一个对象销毁时,会释放这块内存,其他对象的指针就变成悬空指针。所以必须实现深拷贝:
// 拷贝构造函数 String(const String& other) { length = other.length; a = new char[length + 1]; strcpy(a, other.a); } // 赋值运算符重载 String& operator=(const String& other) { if (this == &other) return *this; // 防止自赋值 delete[] a; // 先释放自己原有的内存 length = other.length; a = new char[length + 1]; strcpy(a, other.a); return *this; }
为什么原来的display只输出首字符?
当operator+返回的String对象的a是悬空指针时,它指向的内存已经被系统重新分配或者覆盖了。可能第一个字符还没被覆盖,但后面的内容已经变成了'\0',所以display函数的循环遇到'\0'就停止了,看起来只输出了首字符——这其实是未定义行为的一种表现,换个环境可能会出现完全不同的结果。
备注:内容来源于stack exchange,提问作者Ham Lemon

