vector push_back 操作时引发异常中断

时间:2022-11-09 00:44:09
我最近在把以前一个C语言写的一个成绩管理系统程序移植到C++ 在使用vector保存学生信息时候 push_back引发异常:
一个Student 结构体 一个Manage类(负责管理)
我先贴出两个类部分重要代码先:
Student:
#ifndef _STUDENT_H_
#define _STUDENT_H_
typedef struct student{
string name;
string sex;
int id;
float Chines;
float Math;
float English;
}Student;
#endif


Manage 类:
#ifndef _MANAGE_H_
#define _MANAGE_H_
class Manage{
public:
Manage(string);
Manage(){}
~Manage();
vector<Student>& Read(); //读入文件到顺序容器
vector<Student>& Record(); //手工录入成绩数据
bool Save(string&);   //保存
void Display();  //显示
private:
Manage(Manage& rhs);
Manage& operator=(Manage& r);
vector<Student> m_StuList;   //读入或手工录入数据到顺序容器
string m_FileName;
};
#endif

vector<Student>& Record(); //手工录入成绩数据:
vector<Student>& Manage::Record()
{
cout<<"Please input : Name Sex ID Chinese  Math English and end by enter"<<endl;
Student stu;
for(;;)
{
cin>>stu.name>>stu.sex>>stu.id>>stu.Chines>>stu.Math>>stu.English;
if(stu.name=="end")
{
cout<<"Has Recorded "<<endl;
break;
}
else
m_StuList.push_back(stu);
     }
return m_StuList;
}


vector<Student>& Read(); //读入文件到顺序容器:

vector<Student>& Manage::Read()
{
ifstream infile(m_FileName.c_str(),fstream::in|fstream::binary);
if(infile.is_open()&&infile.good())
{
infile.seekg (0, ios::end);  //获取文件大小
int length = infile.tellg();
infile.seekg (0, ios::beg);
int num = length/sizeof(Student);
m_StuList.clear();
Student Temp;
for (int i = 0;i<num;i++)
{
m_StuList.resize(num);
infile.read((char*)&(m_StuList[i]),
m_StuList.size()*sizeof(Student));


}
infile.close();

}
else
cerr<<"Error : Unable to open the file"<<infile<<endl;
    return m_StuList;
}


bool Save(string&);   //保存:

bool Manage::Save(string& FilePath)
{
ofstream outfile(FilePath.c_str(),fstream::binary|fstream::out);
if (outfile.is_open()&&outfile.good())
{

if (!m_StuList.empty())
{
cout<<"Saving...."<<endl;
int num = m_StuList.size();
for (int i = 0; i<num;i++)
{
outfile.write((char*)(&(m_StuList[i])),sizeof(Student));
}

outfile.close();
}
else
return false;

}
else 
{
cout<<"Save failed~! Unable to open the file :"<<FilePath<<endl;
return false;
}

return true;

}


main中这样调用:
string filePath("f:\\test3.dat");
Manage manage(filePath);
manage.Record();
manage.Save(filePath);
manage.Read();
        manage.Display();

程序运行到 Read中红色部分时候引发了异常 中断了 
中断后跳转到系统内部代码:
for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
*_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
(*_Pnext)->_Myproxy = 0;
_Myproxy->_Myfirstiter = 0;
}


麻烦各位大侠耐心帮忙看看哈

20 个解决方案

#1


string name;
这一类的对象的序列化存在问题。

简单说,你先把string对象写入文件,或者读出,都是一个问题。
你首先要了解string对象的结构,它至少含有一个char*成员。


所以,我问你,你把char* p = "hello world";写入文件,你写入的只是"hello world"的地址值,而不是内容。
所以你的string在Save()和Read()中的写入和读出都是不对的。

你研究一下序列化相关的内容。~

#2


引用 1 楼 healer_kx 的回复:
string name;
这一类的对象的序列化存在问题。

简单说,你先把string对象写入文件,或者读出,都是一个问题。
你首先要了解string对象的结构,它至少含有一个char*成员。


所以,我问你,你把char* p = "hello world";写入文件,你写入的只是"hello world"的地址值,而不是内容。
所以你的string在Save()和Read()……

膜拜一下甘草大款

#3


我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的

#4


帮顶 mark

#5


引用 1 楼 healer_kx 的回复:
string name;
这一类的对象的序列化存在问题。

简单说,你先把string对象写入文件,或者读出,都是一个问题。
你首先要了解string对象的结构,它至少含有一个char*成员。


所以,我问你,你把char* p = "hello world";写入文件,你写入的只是"hello world"的地址值,而不是内容。
所以你的string在Save()和Read(……


我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的

#6


楼主可在异常那行看下i的值是否大于m_StuList.size()

#7


引用 3 楼 pro_charm 的回复:
我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的


那是因为你save了以后就去Read了,如果你这个文件保存了,exe再次启动,恐怕就Read不到什么了。

可靠的做法,就是把name和sex的信息存入前,把他们的字符串长度,按照两个字节或者四个字节先Write到file里面。然后写内容,读的时候,也是如此,先读长度,然后按照长度读内容,写入string对象。

#8


csdn 真烂  想把引发异常那段代码用红色标出来也标不了   
引发异常的是 Read 的
infile.read((char*)&(m_StuList[i]),
m_StuList.size()*sizeof(Student));

#9


原来我是用push_back 的 后来改了用数组形式 还是那段代码

#10


嗯,我觉得lz没有意识到你的做法是完全行不通的,Student对象 可以以你那种方式写入和读出。这个我非常肯定。

#11


引用 7 楼 healer_kx 的回复:
引用 3 楼 pro_charm 的回复:

我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的


那是因为你save了以后就去Read了,如果你这个文件保存了,exe再次启动,恐怕就Read不到什么了。

可靠的做法,就是把name和sex的信息存入前,把他们的字符串长度,按照两个字节或者四个字节先Wr……

可以啊 我后来调试我都是直接从文件读取的 没有手工录入 

#12


我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。

#13


无别的话可说……
十分同意草哥的讲法……

楼主应该好好google一下“为什么要 序列化

#14


建议楼主偷窥一下std::string的源代码.

#15


引用 12 楼 healer_kx 的回复:
我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。

好的 我试下

#16


假设楼主的代码是这样的

#ifndef _STUDENT_H_
#define _STUDENT_H_
typedef struct student{
    char name[32];
    char sex[32];
    int id;
    float Chines;
    float Math;
    float English;
}Student;
#endif


那直接write和read不会有问题。但如果是这样:

#ifndef _STUDENT_H_
#define _STUDENT_H_
typedef struct student{
    char* name;
    char* sex;
    int id;
    float Chines;
    float Math;
    float English;
}Student;
#endif

其中name和sex指向一个new出来的一段内存,那直接write和read还可取么?
std::string也是一样的,不能直接write和read,因为string可能是由指针实现的。

#17


引用 12 楼 healer_kx 的回复:
我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。


我试了 把结果图贴上了吧  测试代码
[img=http://7dr6pa.blu.livefilestore.com/y1pAd4zMdb5UVlV1z3ELQ4TeFlBByvXbQR_IsvXbOl0OecOImu4RaUj9t7c-zOtmlIz8iBS8_gD6ebKuyBTLVMINqAC2Fx4slr4/%E6%9C%AA%E5%91%BD%E5%90%8D2.jpg?psid=1][/img]
 
运行结果:
[img=http://7dr6pa.blu.livefilestore.com/y1pSJd38c7jpevsjJ0Eh5bpSDS2VMo6mNkdFIUNlK23toEQYMlNSEo0I7aNcCAER8HO3hx3ikRpBHoucaerW3iWpWO-Iu-fZrrB/%E6%9C%AA%E5%91%BD%E5%90%8D.jpg?psid=1][/img]

这应该能说明可以读取文件了吧

#18


引用 12 楼 healer_kx 的回复:
我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。


我相信你说的了 我把Student结构体的string对象都去掉 后 就正常了 那为什么上面我测试却对的?

#19


要想保存一个数据结构,首先要清楚这个数据结构的组织形式。对于一个用char指针表示的字符,其数据重点不是char指针的值是多少,而是它指向的那一块内存中存放的字符串。如果相反你把char指针的值保存到文件里,以后加载时再把指针的值读出来,那无异于刻舟求剑。可以肯定的是string类内部一定维护了一个char指针。为什么你之前的测试可以把字符串找回来呢?那是因为进程运行的内存环境没有变化,字符串还在原来的位置,相当于刻舟求剑船还没有开动,所以剑能找回来。

#20


其实我觉得你应该很好想明白。因为你把save写成了这样:


        if (!m_StuList.empty())
        {
            cout<<"Saving...."<<endl;
            int num = m_StuList.size();
            for (int i = 0; i<num;i++)
            {
                outfile.write((char*)(&(m_StuList[i])),sizeof(Student));
            }

            outfile.close();
        }


而没写成这样:

outfile.write((char*)(&m_StuList), sizeof(vector<Student>));


假如string可以直接以二进制写到文件里,那vector又有何不可呢?

#1


string name;
这一类的对象的序列化存在问题。

简单说,你先把string对象写入文件,或者读出,都是一个问题。
你首先要了解string对象的结构,它至少含有一个char*成员。


所以,我问你,你把char* p = "hello world";写入文件,你写入的只是"hello world"的地址值,而不是内容。
所以你的string在Save()和Read()中的写入和读出都是不对的。

你研究一下序列化相关的内容。~

#2


引用 1 楼 healer_kx 的回复:
string name;
这一类的对象的序列化存在问题。

简单说,你先把string对象写入文件,或者读出,都是一个问题。
你首先要了解string对象的结构,它至少含有一个char*成员。


所以,我问你,你把char* p = "hello world";写入文件,你写入的只是"hello world"的地址值,而不是内容。
所以你的string在Save()和Read()……

膜拜一下甘草大款

#3


我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的

#4


帮顶 mark

#5


引用 1 楼 healer_kx 的回复:
string name;
这一类的对象的序列化存在问题。

简单说,你先把string对象写入文件,或者读出,都是一个问题。
你首先要了解string对象的结构,它至少含有一个char*成员。


所以,我问你,你把char* p = "hello world";写入文件,你写入的只是"hello world"的地址值,而不是内容。
所以你的string在Save()和Read(……


我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的

#6


楼主可在异常那行看下i的值是否大于m_StuList.size()

#7


引用 3 楼 pro_charm 的回复:
我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的


那是因为你save了以后就去Read了,如果你这个文件保存了,exe再次启动,恐怕就Read不到什么了。

可靠的做法,就是把name和sex的信息存入前,把他们的字符串长度,按照两个字节或者四个字节先Write到file里面。然后写内容,读的时候,也是如此,先读长度,然后按照长度读内容,写入string对象。

#8


csdn 真烂  想把引发异常那段代码用红色标出来也标不了   
引发异常的是 Read 的
infile.read((char*)&(m_StuList[i]),
m_StuList.size()*sizeof(Student));

#9


原来我是用push_back 的 后来改了用数组形式 还是那段代码

#10


嗯,我觉得lz没有意识到你的做法是完全行不通的,Student对象 可以以你那种方式写入和读出。这个我非常肯定。

#11


引用 7 楼 healer_kx 的回复:
引用 3 楼 pro_charm 的回复:

我仔细测试过了 可以读取出来 也可以显示 就是显示完跳转到了异常中断那里
还有我是整个Student对象以二进制方式写进去的


那是因为你save了以后就去Read了,如果你这个文件保存了,exe再次启动,恐怕就Read不到什么了。

可靠的做法,就是把name和sex的信息存入前,把他们的字符串长度,按照两个字节或者四个字节先Wr……

可以啊 我后来调试我都是直接从文件读取的 没有手工录入 

#12


我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。

#13


无别的话可说……
十分同意草哥的讲法……

楼主应该好好google一下“为什么要 序列化

#14


建议楼主偷窥一下std::string的源代码.

#15


引用 12 楼 healer_kx 的回复:
我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。

好的 我试下

#16


假设楼主的代码是这样的

#ifndef _STUDENT_H_
#define _STUDENT_H_
typedef struct student{
    char name[32];
    char sex[32];
    int id;
    float Chines;
    float Math;
    float English;
}Student;
#endif


那直接write和read不会有问题。但如果是这样:

#ifndef _STUDENT_H_
#define _STUDENT_H_
typedef struct student{
    char* name;
    char* sex;
    int id;
    float Chines;
    float Math;
    float English;
}Student;
#endif

其中name和sex指向一个new出来的一段内存,那直接write和read还可取么?
std::string也是一样的,不能直接write和read,因为string可能是由指针实现的。

#17


引用 12 楼 healer_kx 的回复:
我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。


我试了 把结果图贴上了吧  测试代码
[img=http://7dr6pa.blu.livefilestore.com/y1pAd4zMdb5UVlV1z3ELQ4TeFlBByvXbQR_IsvXbOl0OecOImu4RaUj9t7c-zOtmlIz8iBS8_gD6ebKuyBTLVMINqAC2Fx4slr4/%E6%9C%AA%E5%91%BD%E5%90%8D2.jpg?psid=1][/img]
 
运行结果:
[img=http://7dr6pa.blu.livefilestore.com/y1pSJd38c7jpevsjJ0Eh5bpSDS2VMo6mNkdFIUNlK23toEQYMlNSEo0I7aNcCAER8HO3hx3ikRpBHoucaerW3iWpWO-Iu-fZrrB/%E6%9C%AA%E5%91%BD%E5%90%8D.jpg?psid=1][/img]

这应该能说明可以读取文件了吧

#18


引用 12 楼 healer_kx 的回复:
我说过了,你先Save。而不去Read,
你程序再启动一次,不去Save,而是直接去Read,你试一试,
你的问题就是我10楼说的。。。


我相信你说的了 我把Student结构体的string对象都去掉 后 就正常了 那为什么上面我测试却对的?

#19


要想保存一个数据结构,首先要清楚这个数据结构的组织形式。对于一个用char指针表示的字符,其数据重点不是char指针的值是多少,而是它指向的那一块内存中存放的字符串。如果相反你把char指针的值保存到文件里,以后加载时再把指针的值读出来,那无异于刻舟求剑。可以肯定的是string类内部一定维护了一个char指针。为什么你之前的测试可以把字符串找回来呢?那是因为进程运行的内存环境没有变化,字符串还在原来的位置,相当于刻舟求剑船还没有开动,所以剑能找回来。

#20


其实我觉得你应该很好想明白。因为你把save写成了这样:


        if (!m_StuList.empty())
        {
            cout<<"Saving...."<<endl;
            int num = m_StuList.size();
            for (int i = 0; i<num;i++)
            {
                outfile.write((char*)(&(m_StuList[i])),sizeof(Student));
            }

            outfile.close();
        }


而没写成这样:

outfile.write((char*)(&m_StuList), sizeof(vector<Student>));


假如string可以直接以二进制写到文件里,那vector又有何不可呢?

#21