真奇怪,为什么这个地方出错了?

时间:2021-05-04 04:18:38
为了加深对观察者模式的理解,我仿照别人的Java程序,写了一个对应的C++程序,如下,但是结果很奇怪,而且调试后发现了一个更奇怪的现象,百思不得其解,不知哪位高手能帮忙解答。
程序如下:
#include <iostream>
#include <vector>
#include <list>
using namespace std;

class Subjects;
class Observe
{
public:
virtual void update(Subjects* t)=0;
virtual char* getName()=0;
};

class Subjects
{
public:
virtual void addobs(Observe* o)=0;
virtual char* getPhone()=0;
virtual void notice()=0;
};

class Teacher;

class Student:public Observe
{
public:
Student(char* _name)
{
name =_name;
}
char* getName()
{
return name;
}
void update(Subjects* t)
{
phone = t->getPhone();
show();
}
void show()
{
cout<<getName()<<":The teacher's phone number is "<<phone<<endl;
}
private:
char* name;
char* phone;
};

class Teacher:public Subjects
{
public:
Teacher()
{
phone = "";
}
void addobs(Observe* o)
{
st.push_back(o);
}
void setPhone(char* phone)
{
this->phone = phone;
notice();
}
char* getPhone()
{
return phone;
}
void notice()
{
for(vector<Observe*>::iterator iter = st.begin();iter!=st.end();iter++)
{
(*iter)->update(this);
}
}
private:
char* phone;
vector<Observe*> st;
};

int main(int argc,char* argv[])
{
Teacher* teacher = new Teacher();
int i;
for(i=0;i<10;i++)
{
char strName[20];
sprintf(strName,"student%d",i+1);//这一句出错了,太奇怪了
Student* stu = new Student(strName);
teacher->addobs(stu);
}

teacher->setPhone("68948888");
cout<<"After setPhone(\"68947777\")"<<endl;
teacher->setPhone("68947777");
}
期望结果为:
student1:The teacher's phone number is 68948888
student2:The teacher's phone number is 68948888
student3:The teacher's phone number is 68948888
student4:The teacher's phone number is 68948888
student5:The teacher's phone number is 68948888
student6:The teacher's phone number is 68948888
student7:The teacher's phone number is 68948888
student8:The teacher's phone number is 68948888
student9:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
After setPhone("68947777")
student1:The teacher's phone number is 68947777
student2:The teacher's phone number is 68947777
student3:The teacher's phone number is 68947777
student4:The teacher's phone number is 68947777
student5:The teacher's phone number is 68947777
student6:The teacher's phone number is 68947777
student7:The teacher's phone number is 68947777
student8:The teacher's phone number is 68947777
student9:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
实际结果却是:
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
student10:The teacher's phone number is 68948888
After setPhone("68947777")
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777
student10:The teacher's phone number is 68947777

经过调试以后,发现出错语句竟然在sprintf(strName,"student%d",i+1);我在这一句设了断点,发现在次之前,st中的各个元素是正确的,执行这一句之后就出错了。举个例子,当第一次循环(即i=0)时,执行完teacher->addobs(stu)后,st中含有一个指针元素,其name为“student1”,即st[0]->name=“student1”;第二次循环时,当执行完sprintf(strName,"student%d",i+1)后,竟然发现st[0]->name=“student2”。当第二次循环执行完毕后,结果自然变为了st[0]->name=“student2”,st[1]->name=“student2”,而第三次循环执行完出错的那一句后,结果同样奇怪地变为了st[0]->name=“student3”,st[1]->name=“student3”,这是为什么?sprintf(strName,"student%d",i+1)是怎么影响到st的?

7 个解决方案

#1


Student(char* _name) 

name =_name; 
}

char*的复制不要这样做,用strncpy
我估计是由于所有的name都指向的是main里面的char strName[20]所导致

#2


char strName[20]; 
你这就这个一个数组,写来写去,不就重复在这些么,不久覆盖了。
结果就是最后那个数 即9+1=10;

#3


name是指针不是数组所以改一处就会影响其他

#4


引用 2 楼 chin_chen 的回复:
char strName[20];
你这就这个一个数组,写来写去,不就重复在这些么,不久覆盖了。
结果就是最后那个数 即9+1=10;


改下试试

char* strName[10][20];
sprintf(strName[i],"student%d",i+1);//后面打印的时候也要相应的改下。

#5


改为 
char *strName = new char[20]; 

#6


原因有两个:
1.
class Student:public Observe
{
public:
Student(char* _name)
{
name =_name;
}
...
private:
char* name;
char* phone;
};

在你的代码里,Student的构造函数将一个指针传给了char* name
这样做是非常危险的,因为你传的指针随时可能在其他代码被更改。这样你建立的这个student对象里面的name也随时可能被改掉。
而且有可能这个指针后来在别的地方被释放掉了,接下来再访问这个name就是没有意义的。

2.
for(i=0;i<10;i++)
{
char strName[20];
sprintf(strName,"student%d",i+1);//这一句出错了,太奇怪了
Student* stu = new Student(strName);
teacher->addobs(stu);
}

在这里有一个局部的char数组。每次for循环进入,这个数组strName[20]都是在相同的内存地址,比如设为0x12345678。
也就是说,你每进入一次循环,就把内存0x12345678的字符串重新格式化了,然后把这个地址赋给了一个student。
这样的话,循环结束后,所有10个student对象里面的name都指着这个0x12345678的字符串,所以都是"student10".

第二个问题也是由第一部分引起的。对象里面的char*不能这样直接赋值,你可以考虑使用std::string代替。

#7


1 char name[200];

2 重复擦写了strName

#1


Student(char* _name) 

name =_name; 
}

char*的复制不要这样做,用strncpy
我估计是由于所有的name都指向的是main里面的char strName[20]所导致

#2


char strName[20]; 
你这就这个一个数组,写来写去,不就重复在这些么,不久覆盖了。
结果就是最后那个数 即9+1=10;

#3


name是指针不是数组所以改一处就会影响其他

#4


引用 2 楼 chin_chen 的回复:
char strName[20];
你这就这个一个数组,写来写去,不就重复在这些么,不久覆盖了。
结果就是最后那个数 即9+1=10;


改下试试

char* strName[10][20];
sprintf(strName[i],"student%d",i+1);//后面打印的时候也要相应的改下。

#5


改为 
char *strName = new char[20]; 

#6


原因有两个:
1.
class Student:public Observe
{
public:
Student(char* _name)
{
name =_name;
}
...
private:
char* name;
char* phone;
};

在你的代码里,Student的构造函数将一个指针传给了char* name
这样做是非常危险的,因为你传的指针随时可能在其他代码被更改。这样你建立的这个student对象里面的name也随时可能被改掉。
而且有可能这个指针后来在别的地方被释放掉了,接下来再访问这个name就是没有意义的。

2.
for(i=0;i<10;i++)
{
char strName[20];
sprintf(strName,"student%d",i+1);//这一句出错了,太奇怪了
Student* stu = new Student(strName);
teacher->addobs(stu);
}

在这里有一个局部的char数组。每次for循环进入,这个数组strName[20]都是在相同的内存地址,比如设为0x12345678。
也就是说,你每进入一次循环,就把内存0x12345678的字符串重新格式化了,然后把这个地址赋给了一个student。
这样的话,循环结束后,所有10个student对象里面的name都指着这个0x12345678的字符串,所以都是"student10".

第二个问题也是由第一部分引起的。对象里面的char*不能这样直接赋值,你可以考虑使用std::string代替。

#7


1 char name[200];

2 重复擦写了strName