VS集成Qt中调用类函数触发Debug错误的问题求助
在VS集成的Qt应用中,调用基类指针指向的派生类对象的任意函数时程序都会崩溃,弹出Debug错误对话框。例如调用getFile()本应返回字符串,但程序直接崩溃;尝试改用char*替代string时,得到的是垃圾值而非实际数据。
使用基类指针数组notes* allNotes[1000];存储派生类对象,调用如下代码时触发错误:
void dashboard::receiveDisplayImage(int num) { string kk = allNotes[num]->getFile(); img = new image(this); img->finallyImageOutput(kk.c_str()); }
单独检查过类的信息存储正常,数组也未越界。此前直接用vector<schoolNotes>和vector<personalNotes>存储对象时程序正常,改用指针后出现崩溃。
相关完整代码如下:
#include<iostream> #include<string> #include<vector> #include<stdlib.h> #include <fstream> #include <typeinfo> using namespace std; enum p_type { sclN, persN, n }; class notes { public: notes(); notes(string title, string date, string t); string getFile() { return fp;} virtual p_type get_type(); private: string title; string date; string fp; }; class personalNotes : public notes { private: public: personalNotes(); personalNotes(string title, string date, string t); }; class schoolNotes : public notes { private: string category; string topicName; string subject; public: schoolNotes(); schoolNotes(string Cat, string Topic, string sub, string title, string date, string t); }; p_type notes::get_type() { if ( typeid(*this) == typeid(schoolNotes)) return sclN; else if ( typeid(*this) == typeid(personalNotes)) return persN; else { return n; //cerr << "\nNo such type"; exit(1); } } notes::notes(string Title, string Date, string t) :title(Title), date(Date), fp(t) { } personalNotes::personalNotes(string title, string date, string t) :notes(title, date, t) { } class dashboard { public: dashboard(); string tf; void addPersonalNote(string title, string date); int rownumber; void submitFile(); void writeToFile(); notes* allNotes[1000]; }; void dashboard::writeToFile() { int size; ofstream ouf; p_type ptype; ouf.open("user.txt", ios::binary | ios::trunc); if (!ouf) { cout << "\nCan't open file\n"; return; } for (int j = 0; j < rownumber; j++) { ptype = allNotes[j]->get_type(); ouf.write((char*)&ptype, sizeof(ptype)); switch (ptype) { case sclN: size = sizeof(schoolNotes); break; case persN: size = sizeof(personalNotes); break; } ouf.write((char*)(allNotes[j]), size); if (!ouf) { cout << "\nCan't write to file\n"; return; } } ouf.close(); } dashboard::dashboard() : rownumber(0) { } void dashboard::addPersonalNote(string title, string date) { personalNotes tempPers(title, date, tf); allNotes[rownumber]=&tempPers; rownumber++; } void dashboard::submitFile() { tf = "this is a string"; } int main() { dashboard d; d.submitFile(); d.addPersonalNote("this is title", "this is date"); d.writeToFile(); }
核心问题:悬挂指针(野指针)
崩溃的直接原因是addPersonalNote函数中创建的tempPers是局部栈对象,函数执行完毕后,该对象会被销毁,内存被回收。此时allNotes[rownumber]存储的是指向已销毁对象的指针,后续调用allNotes[num]->getFile()时,访问的是已经释放的内存,导致未定义行为(崩溃或读取垃圾值)。
修复步骤
1. 改用动态分配对象解决悬挂指针问题
修改addPersonalNote函数,使用new在堆上创建对象,确保对象生命周期不受函数栈帧影响:
void dashboard::addPersonalNote(string title, string date) { // 使用new在堆上创建对象,避免局部对象销毁导致指针失效 personalNotes* tempPers = new personalNotes(title, date, tf); allNotes[rownumber] = tempPers; rownumber++; }
注意:后续需要在适当的时机(如程序退出、dashboard销毁时)遍历allNotes数组,用delete释放每个对象,避免内存泄漏。
2. 修复二进制文件写入的问题
当前writeToFile函数直接将类对象写入二进制文件的方式不可行,因为类中包含std::string成员——std::string内部是指向堆内存的指针,直接写入文件只会存储指针地址,而非实际字符串内容。需要实现序列化逻辑,将字符串的长度和内容分别写入:
给基类notes添加虚函数serialize,派生类重写该函数完成自身的序列化:
class notes { public: // ... 已有成员 ... virtual void serialize(ofstream& ouf) const { // 写入title size_t len = title.size(); ouf.write((char*)&len, sizeof(len)); ouf.write(title.c_str(), len); // 写入date len = date.size(); ouf.write((char*)&len, sizeof(len)); ouf.write(date.c_str(), len); // 写入fp len = fp.size(); ouf.write((char*)&len, sizeof(len)); ouf.write(fp.c_str(), len); } virtual ~notes() = default; // 虚析构函数,确保派生类析构被正确调用 }; class schoolNotes : public notes { public: // ... 已有成员 ... void serialize(ofstream& ouf) const override { notes::serialize(ouf); // 先序列化基类成员 // 写入schoolNotes特有的成员 size_t len = category.size(); ouf.write((char*)&len, sizeof(len)); ouf.write(category.c_str(), len); len = topicName.size(); ouf.write((char*)&len, sizeof(len)); ouf.write(topicName.c_str(), len); len = subject.size(); ouf.write((char*)&len, sizeof(len)); ouf.write(subject.c_str(), len); } }; class personalNotes : public notes { public: // ... 已有成员 ... void serialize(ofstream& ouf) const override { notes::serialize(ouf); // 基类成员已足够,无需额外写入 } };
然后修改writeToFile函数,调用虚函数完成序列化:
void dashboard::writeToFile() { ofstream ouf; p_type ptype; ouf.open("user.txt", ios::binary | ios::trunc); if (!ouf) { cout << "\nCan't open file\n"; return; } // 先写入笔记数量,方便后续读取 ouf.write((char*)&rownumber, sizeof(rownumber)); for (int j = 0; j < rownumber; j++) { ptype = allNotes[j]->get_type(); ouf.write((char*)&ptype, sizeof(ptype)); // 调用虚函数完成序列化 allNotes[j]->serialize(ouf); if (!ouf) { cout << "\nCan't write to file\n"; return; } } ouf.close(); }
3. 补充析构函数避免内存泄漏
在dashboard的析构函数中释放所有动态分配的对象:
class dashboard { public: // ... 已有成员 ... ~dashboard() { for (int j = 0; j < rownumber; j++) { delete allNotes[j]; allNotes[j] = nullptr; } } };
额外建议
- 优先使用
std::vector<std::unique_ptr<notes>>替代原始指针数组,智能指针可以自动管理内存,避免手动delete导致的内存泄漏或悬空指针问题。 - 二进制文件读取时也需要对应实现反序列化逻辑,确保能正确恢复对象的所有成员。
内容的提问来源于stack exchange,提问作者mehreen hossain

