我最近对c ++代码有一个非常奇怪的问题。 我以简约的例子再现了这个案例。 我们有一个Egg课程:
class Egg
{
private:
const char* name;
public:
Egg() {};
Egg(const char* name) {
this->name=name;
}
const char* getName() {
return name;
}
};
我们还有一个篮子类来举行鸡蛋
const int size = 15;
class Basket
{
private:
int currentSize=0;
Egg* eggs;
public:
Basket(){
eggs=new Egg[size];
}
void addEgg(Egg e){
eggs[currentSize]=e;
currentSize++;
}
void printEggs(){
for(int i=0; i<currentSize; i++)
{
cout<<eggs[i].getName()<<endl;
}
}
~Basket(){
delete[] eggs;
}
};
所以这里是按预期工作的示例。
Basket basket;
Egg egg1("Egg1");
Egg egg2("Egg2");
basket.addEgg(egg1);
basket.addEgg(egg2);
basket.printEggs();
//Output: Egg1 Egg2
这是预期的结果,但是如果我想根据一些循环变量添加带有生成名称的N个蛋,我有以下问题。
Basket basket;
for(int i = 0; i<2; i++) {
ostringstream os;
os<<"Egg"<<i;
Egg egg(os.str().c_str());
basket.addEgg(egg);
}
basket.printEggs();
//Output: Egg1 Egg1
如果我将循环条件改为i <5,我得到“Egg4 Egg4 Egg4 Egg4 Egg4”。它将最后添加的Egg保存在动态Egg数组的所有索引中。
在google中进行一些搜索之后,我发现在Egg中给char * name变量一个固定的大小并在构造函数中使用strcpy
修复了这个问题。
这是“固定的”蛋类。
class Egg
{
private:
char name[50];
public:
Egg(){};
Egg(const char* name)
{
strcpy(this->name, name);
}
const char* getName()
{
return name;
}
};
现在的问题是为什么? :d
提前致谢。
Here是整个代码的链接。
答案 0 :(得分:5)
让我们仔细看看这个表达式:
os.str().c_str()
。
函数str
按值返回字符串,并以此方式使用它使返回的字符串成为临时对象,其生命周期仅到结束时表达方式。表达式结束后,字符串对象将被破坏,不再存在。
传递给构造函数的指针是指向临时字符串对象的内部字符串的指针。一旦字符串对象被破坏,指针不再有效,并且使用它将导致未定义的行为。
当您想要使用字符串时,简单的解决方案当然是使用std::string
。更复杂的解决方案是使用数组并复制字符串的内容,然后消失(就像在“固定”Egg
类中一样)。但请注意,使用固定大小数组的“固定”解决方案容易出现缓冲区溢出。
答案 1 :(得分:2)
在第一种情况下,你复制指向字符串的指针。
在第二种情况下,使用strcpy()
,您实际上深层复制字符串。
ostringstream
创建的字符串。当超出范围时会发生什么?
未定义的行为!
答案 2 :(得分:1)
os.str()
是std::string
类型的匿名临时,以及访问.c_str()
指向的内存的行为,一旦该匿名临时消失范围(在语句末尾), undefined 。您的第二种情况有效,因为strcpy(this->name, name);
在临时超出范围之前正在获取.c_str()
指向的数据的副本。但代码仍然很脆弱:固定大小的字符缓冲区很容易被溢出。 (一个简单的解决方法是使用strncpy
)。
但要正确修复,请利用C ++标准库:使用std::string
作为name
的类型,const std::string&
作为getName
的返回类型,并使用容器,如std::list<Egg>
,将鸡蛋放在篮子里。
答案 3 :(得分:0)
你不要在你的Egg
构造函数中复制字符串,只是一个指针,它是字符串的起始地址。
碰巧,你的ostrings的所有实例一次又一次地在同一个地方分配缓冲区。碰巧在构造for
循环和输出打印for
循环之间没有覆盖缓冲区。
这就是为什么最终所有Egg
的{{1}}指针指向同一个地方,并且该地方包含建立的姓氏。