注:本文是公司同事的一个分享,由于很有代表性,特分享在此,希望对大家有用。 上次welkin在处理一个豆瓣的cgi时遇到1个奇怪的问题,就是对一个string对象的修改引起了另一个string对象的同步修改。后来定位到原有,是因为有函数对string对象的buf内容直接进行了操作,破坏了“写时拷贝”的规则。下面这个例子说明了问题是如何产生的,已经如何避免:
int main()
{
string str1 = "abcd";
string str2 = str1;
char *p1 = const_cast<char*>(str1.c_str());
p1[0] = 'o';
//这里str1和str2同时被修改了
printf("%s %s\n", str1.c_str(), str2.c_str());
string str3 = "abcd";
string str4 = str3;
char *p2 = &(str3[0]);
p2[0] = 'o';
//这里只有str3被修改,str4不变
printf("%s %s\n", str3.c_str(), str4.c_str());
return 0;
}
上面的程序的运行结果如下:
obcd obcd
obcd abcd
str1和str2被同时改变这个比较好理解。因为str2的初值为str1,根据“写时拷贝”原则,在str2的值或者str1的值不发生改变的时候,2个对象实际上指向了同一块内存地址。 然而我们使用c_str方法取出了str1的buf首地址,且强制转换成了char*类型,这个强制转换的操作就是破坏C++标准的罪魁祸首了。它使得编译器无法获知通过指针p1对buf内容的修改操作而自动进行拷贝操作,结果就是str1和str2对象都被修改了。 而str3和str4为什么没有被同时修改呢。原因是指针p2的取值方式,它使用了[]操作符。C++标准认为,当你通过迭代器或[]获取到string的内部地址的时候,string并不知道你将是要读还是要写。这是它无法确定,为此,当你获取到内部引用后,为了避免不能捕获你的写操作,它在此时废止了“写时拷贝”技术! 实际上,将const char*强制转换为char*类型是问题的根本原因,他破坏了C++标准,我们应该尽量避免这么做。但现实是我们已经有很多接口这样实现了,这是一种典型的像C一样使用C++的行为。所以如果你遇到这种情况,记得使用[]操作符来获取string的第一个字符的地址,而不要使用c_str方法。
dazuiniu on #
代码贴重了?
Reply
Dante on #
啊,谢谢提醒,已经修正……
明明写完的时候还是好的,汗……
Reply
游客 on #
c_str()返回const char *就是防君子不防小人,const_cast去除const的话,那后果只能自负了。
Reply
Dante on #
奇怪,评论居然跑到过滤列表去了,还好今天人工看了一下~~
Reply
Terrence on #
应该完全按照C++的逻辑来写C++程序才好~
Reply
Dante on #
呵呵,如果只是把string类型当做字符串来用的话确实不会有问题,不过有时候也会把string当作buf空间来用,即用resize()方法来指定buff大小并copy数据。所以有时候还是可能会遇到这种问题的~~~
Reply
dazuiniu on #
谁让C++没有直接操作raw buffer 的类呢。用 char buf[] 会有buffer overflow的危险,用你说的string也不是很直接。
Reply
Dante on #
恩,用string的话确实用起来不太直观,容易出问题~不过在某些场景下用string来控制buf还是不错的,呵呵
Reply
雨碎江南 on #
C++ , CGI 这两个极易出问题的东西...
估计我是用不到CGI了.
至于C++,还是等到:
(1)高中毕业
(2)学通汇编和编译原理
之后在研究把吧..呵呵.
Reply
雨碎江南 on #
哦.竟然打错字了...
Reply
Dante on #
哈哈,CGI确实不是每个领域都会用到,不过C++还是学一下比较好,很有用的一门语言~~
Reply
alexandercer on #
"所以如果你遇到这种情况,记得使用[]操作符来获取string的第一个字符的地址,而不要使用c_str方法。" 这句话说的很好....
Reply
Dante on #
呵呵~~有些问题确实要知道内部实现才能知其所以然~
Reply