我们知道,string类为我们提供了很多的便利,我们用string类能方便的做字符串的各种操作,避免了我们直接操作char指针而产生的一些繁琐的操作,和内存泄露的可能性,可是string类也是有陷阱的。下面我们看这样一个例子,你能看出问题所在吗?
复制代码
string getString()
{
return "just a test";
}
void test()
{
const char *pc = getString().c_str();
cout<<pc<<endl;
}
复制代码
如果看不出来可以实际运行一下,会发现输不出结果来,这是为什么呢?
当我们写getString函数时,如果其返回类型是const char*我们可能会分外小心,因为我们知道"just a test"生命会在函数返回后结束,我们可能会new一段空间来放这个字符串,然后返回指针,或者是要求调用者给一个空间来放这个字符串。但是当返回类型是string时,我们就不会如此小心翼翼,因为我们知道string类实现了拷贝构造函数和赋值函数。它会为我们操心空间分配和销毁的问题。所以在这里,我们直接返回"just a test"字符串是没有问题的。
接着我们对一个string对象调用c_str函数貌似也没有什么问题,因为这是string类提供给我们的功能。但是这里的关键是,我们没有将getString()的结果赋给一个string对象就直接获取其指针了,这时,系统并不会为string调用拷贝构造函数或是赋值函数,返回的string仍然只是一个临时对象的状态,它会在完成对pc的赋值后被销毁,这时其内部的数据也不会存在了。
怎么解决这个问题呢?
我们要改变c_str函数,让它不要直接返回指针,而是new一段空间来放这个字符串再返回指针吗?这样,会降低效率,且每个调用c_str函数的用户都需要操心delete指针的问题,很容易造成内存泄露。
我们要改写getString函数,重新为返回的字符串new一段空间吗?这么做显然也不合适。
这时,我们有两种比较可行的解决方法。
1.不要直接返回临时对象的指针,将临时对象先赋值给一个局部对象,再获取其指针。这样,将上例改写为
void test()
{
string str = getString();
const char *pc = str.c_str();
cout<<pc<<endl;
}
2.如果要使用临时对象的指针,将所有的使用放到一个语句里进行。这样,将上例改写为
void test()
{
cout<<getString().c_str()<<endl;
}