我正在阅读“C ++ Strategies and Tactics”一书,并对以下代码感到困惑。作者说,手术可能不安全。但是,我还没有理由。你能救我吗?
#include <iostream>
#include <string.h>
void remove_blanks(char* cp)
{
char*p = cp;
while(*p)
{
if(*p != ' ')
*cp++ = *p;
++p;
}
*cp = '\0';
}
class String
{
public:
String(char* p = " "):str(new char[strlen(p) + 1])
{
strcpy(str,p);
}
~String()
{
delete []str;
}
operator const char* () const
{
return (const char*)str;
}
private:
char* str;
};
int main()
{
String s("hello world");
remove_blanks((char*)(const char*)s);
std::cout << s << ".\n";
}
答案 0 :(得分:6)
在remove_blanks中修改字符串(在链接的强制转换之后)是明确的未定义行为:
来自7.1.6.1 cv-qualifiers 指向cv限定类型的指针或引用无需实际指向或引用
到一个cv限定的对象,但它被视为它;一个 const限定访问路径即使不能用于修改对象 引用的对象是非const对象,可以修改 通过其他一些访问路径。 [注意:Cv限定符受到支持 类型系统,以便它们不会在没有铸造的情况下被颠覆 (5.2.11)。 - 结束注释]除了任何类成员声明可变 (7.1.1)可以修改,任何修改const对象的尝试 它的生命周期(3.8)导致未定义的行为。
答案 1 :(得分:0)
我花了很多时间阅读并重新阅读该循环,但我认为问题是:
C ++类String
拥有其私有成员str
指向的内存。一旦外部代码使用类型转换运算符来获取该指针的副本,它就有 no 方式告知基础缓冲区何时被delete[]
编辑。
更改str
指向的内存也可能使指向同一缓冲区的任何其他未完成指针无效。缩短字符串可能会使其他指针已经指向字符串的NULL终止符。 (虽然在这个具体的例子中,原作&#39; \ 0&#39;仍然存在)。
删除String
后,堆上的不同分配可能会重用该内存。之后调用remove_blanks()
将导致难以调试的灾难。
可以通过以下方式直接使用strcpy()
创建字符串的新副本,使用std::shared_ptr<char *>
,或使remove_blanks()
成为String
的成员函数并执行此操作来避免这种情况完全放弃这些指针。
答案 2 :(得分:0)
感谢您的回答。
一开始,我认为问题是前一个str
是"hello world\0"
而后一个str
是"helloworld\0\0"
,如果delete []str
不是'\0'
停止释放内存直到第一个memory leak
,它将导致"delete operator"
。
当我发现str
根据&#34; 4 byte int
&#34; re-delete
释放内存{{1}}时,会发现错误在那段记忆之前,我意识到我已经想得太多了。
现在我认为问题是它可能导致{{1}}内存str指向。