使用重载运算符时控制台输出文本异常的问题排查
问题:C++自定义text类输出乱码
我实现了一个带有word缓冲区的text类,通过重载+运算符追加传入的字符串。程序基本能运行,但控制台输出会出现乱码字节,无法定位问题。
我尝试过修改构造函数和重载的+运算符,分别在word缓冲区的开头和末尾添加'\0',但问题仍未解决。
原代码
#include <cstdlib> #include <iostream> using namespace std; class text { const char *word; int size; public: text(int sz); text(const text &cpytxt); ~text() { delete [] word; } text &operator+(const char *str); void operator!(); void operator--(); }; text::text(int sz = 80) { if(sz <= 0) { cout << "Error: sz <= 0" << '\n'; exit(1); } size = sz; word = new char[sz]; } text::text(const text &cpytxt) { if(cpytxt.size <= 0) { cout << "Error: cpytxt.size <= 0" << '\n'; exit(1); } size = cpytxt.size; word = new char[cpytxt.size]; } text &text::operator+(const char *str) { char *tmpstr = (char *) str; // Saves constant direction into non-constant direction for later usage char *tmpwrd = (char *) word; // Same with the object buffer int i = 0; // Creates 'i' iterator while(*tmpwrd) { // Sets word direction and 'i' poiting to the last empty byte ++tmpwrd; ++i; } // Assigns each char from the string to the last empty byte of word and consecutive empty bytes for(; i < size && *tmpstr; ++i, ++tmpwrd, ++tmpstr) { if(i == size - 1) *tmpwrd = '\0'; else *tmpwrd = *tmpstr; } return *this; // Returns invoking object } void text::operator!() { cout << word; } void text::operator--() { char *tmpwrd = (char *) word; for(int i = 0; i < size; ++i, ++tmpwrd) { *tmpwrd = '\0'; } } int main() { text myTxt(1000); myTxt + "Hello World!" + " This is an example!"; !myTxt; --myTxt; myTxt + "GOODBYE!"; !myTxt; --myTxt; system("pause"); return 0; }
问题根源及修复方案
1. 缓冲区未初始化导致乱码
构造函数中new char[sz]分配的内存是未初始化的,里面是随机垃圾值。第一次调用operator+时,while(*tmpwrd)会一直读取随机内存,直到遇到某个恰好是'\0'的字节,这会导致越界访问、写入位置错误,最终输出乱码。
修复:构造时用值初始化语法将缓冲区设为全'\0':
text::text(int sz = 80) { if(sz <= 0) { cout << "Error: sz <= 0" << '\n'; exit(1); } size = sz; // 值初始化自动将所有字节设为0 word = new char[sz](); }
2. 拷贝构造函数未复制内容
当前拷贝构造函数只分配了内存,没有复制原对象的word内容,若后续用到拷贝构造(比如对象赋值、传参)会导致数据丢失或错误。
修复:添加内容复制逻辑:
#include <cstring> // 需包含strncpy的头文件 text::text(const text &cpytxt) { if(cpytxt.size <= 0) { cout << "Error: cpytxt.size <= 0" << '\n'; exit(1); } size = cpytxt.size; word = new char[size](); // 复制原对象内容,预留一个位置给终止符 strncpy((char*)word, cpytxt.word, size-1); }
3. operator+逻辑缺陷
原operator+中,当填充到size-1位置时直接设'\0',但如果字符串未写完会截断;且循环结束后没有确保缓冲区末尾一定有'\0',可能导致输出时读取到垃圾值。
修复:调整逻辑,确保无论是否填满都添加终止符:
text &text::operator+(const char *str) { if(!str) return *this; // 空字符串直接返回 char *tmpwrd = (char *) word; const char *tmpstr = str; // 无需强制转非const,仅读取 // 定位到当前字符串末尾 while(*tmpwrd) { ++tmpwrd; } // 计算剩余可写入空间(留一个位置给'\0') int remaining = size - (tmpwrd - (char*)word) - 1; if(remaining <= 0) return *this; // 复制字符串内容 while(remaining > 0 && *tmpstr) { *tmpwrd++ = *tmpstr++; remaining--; } // 强制添加终止符 *tmpwrd = '\0'; return *this; }
4. 优化清空操作
原operator--用循环清空缓冲区,可替换为更高效的memset:
void text::operator--() { char *tmpwrd = (char *) word; memset(tmpwrd, 0, size); }
修复后完整代码
#include <cstdlib> #include <iostream> #include <cstring> using namespace std; class text { const char *word; int size; public: text(int sz = 80); text(const text &cpytxt); ~text() { delete [] word; } text &operator+(const char *str); void operator!(); void operator--(); }; text::text(int sz) { if(sz <= 0) { cout << "Error: sz <= 0" << '\n'; exit(1); } size = sz; word = new char[sz](); } text::text(const text &cpytxt) { if(cpytxt.size <= 0) { cout << "Error: cpytxt.size <= 0" << '\n'; exit(1); } size = cpytxt.size; word = new char[size](); strncpy((char*)word, cpytxt.word, size-1); } text &text::operator+(const char *str) { if(!str) return *this; char *tmpwrd = (char *) word; const char *tmpstr = str; while(*tmpwrd) { ++tmpwrd; } int remaining = size - (tmpwrd - (char*)word) - 1; if(remaining <= 0) return *this; while(remaining > 0 && *tmpstr) { *tmpwrd++ = *tmpstr++; remaining--; } *tmpwrd = '\0'; return *this; } void text::operator!() { cout << word << endl; } void text::operator--() { char *tmpwrd = (char *) word; memset(tmpwrd, 0, size); } int main() { text myTxt(1000); myTxt + "Hello World!" + " This is an example!"; !myTxt; --myTxt; myTxt + "GOODBYE!"; !myTxt; --myTxt; system("pause"); return 0; }
内容的提问来源于stack exchange,提问作者Tobías Milano
相关产品推荐
相关产品推荐

