我一直试图想出这个问题好几个小时了,而且我已经结束了。如果有人能在我做错的时候告诉我,我当然会感激。
我编写了一个简单的类来模拟字符串的基本功能。该类的成员包括一个字符指针 data (指向一个动态创建的char数组)和一个整数 strSize (它包含字符串的长度,sans终止符。)< / p>
由于我使用的是新和删除,因此我实现了复制构造函数和析构函数。当我尝试实现运算符+ = 时出现问题。 LHS对象正确构建新字符串 - 我甚至可以使用cout打印它 - 但是当我尝试在析构函数中释放数据指针时出现问题:我在指向的内存地址处得到“正常块后检测到的堆损坏”通过 data 数组,析构函数试图解除分配。
这是我完整的课程和测试程序:
#include <iostream>
using namespace std;
// Class to emulate string
class Str {
public:
// Default constructor
Str(): data(0), strSize(0) { }
// Constructor from string literal
Str(const char* cp) {
data = new char[strlen(cp) + 1];
char *p = data;
const char* q = cp;
while (*q)
*p++ = *q++;
*p = '\0';
strSize = strlen(cp);
}
Str& operator+=(const Str& rhs) {
// create new dynamic memory to hold concatenated string
char* str = new char[strSize + rhs.strSize + 1];
char* p = str; // new data
char* i = data; // old data
const char* q = rhs.data; // data to append
// append old string to new string in new dynamic memory
while (*p++ = *i++) ;
p--;
while (*p++ = *q++) ;
*p = '\0';
// assign new values to data and strSize
delete[] data;
data = str;
strSize += rhs.strSize;
return *this;
}
// Copy constructor
Str(const Str& s)
{
data = new char[s.strSize + 1];
char *p = data;
char *q = s.data;
while (*q)
*p++ = *q++;
*p = '\0';
strSize = s.strSize;
}
// destructor
~Str() { delete[] data; }
const char& operator[](int i) const { return data[i]; }
int size() const { return strSize; }
private:
char *data;
int strSize;
};
ostream& operator<<(ostream& os, const Str& s)
{
for (int i = 0; i != s.size(); ++i)
os << s[i];
return os;
}
// Test constructor, copy constructor, and += operator
int main()
{
Str s = "hello"; // destructor for s works ok
Str x = s; // destructor for x works ok
s += "world!"; // destructor for s gives error
cout << s << endl;
cout << x << endl;
return 0;
}
编辑:加速C ++问题12-1。
答案 0 :(得分:4)
以下代码块使p指向数组旁边。
while (*p++ = *q++) ;
*p = '\0';
您在复制构造函数中使用的更好(和安全)的解决方案:
while (*q)
*p++ = *q++;
*p = '\0';
答案 1 :(得分:3)
这里已经有很多好的答案,但值得插入Valgrind作为解决这类问题的工具。如果您可以访问* nix框,Valgrind工具可以成为真正的救星。
只是为了告诉你,这是我在编译和运行你的程序时得到的:
% g++ -g -o test test.cpp % valgrind ./test ==2293== Memcheck, a memory error detector ==2293== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2293== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info ==2293== Command: ./test ==2293== ==2293== Invalid write of size 1 ==2293== at 0x8048A9A: Str::operator+=(Str const&) (test.cpp:36) ==2293== by 0x8048882: main (test.cpp:82) ==2293== Address 0x42bc0dc is 0 bytes after a block of size 12 alloc'd ==2293== at 0x4025024: operator new[](unsigned int) (vg_replace_malloc.c:258) ==2293== by 0x8048A35: Str::operator+=(Str const&) (test.cpp:26) ==2293== by 0x8048882: main (test.cpp:82) ==2293== helloworld! hello ==2293== ==2293== HEAP SUMMARY: ==2293== in use at exit: 0 bytes in 0 blocks ==2293== total heap usage: 4 allocs, 4 frees, 31 bytes allocated ==2293== ==2293== All heap blocks were freed -- no leaks are possible ==2293== ==2293== For counts of detected and suppressed errors, rerun with: -v ==2293== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 17 from 6) %
你可以看到它指出了另一个答案在这里指出的线条(靠近第36行)。
答案 2 :(得分:2)
while (*p++ = *i++) ; // the last iteration is when i is one past the end
// i is two past the end here -- you checked for a 0, found it, then incremented past it
p--; //here you corrected for this
while (*p++ = *q++) ;// the last iteration is when p and q are one past the end
// p and q are two past the end here
// but you didn't insert a correction here
*p = '\0'; // this write is in unallocated memory
使用类似于您在复制构造函数中使用的习惯用语:
while (*i) *p++ = *i++; //in these loops, you only increment if *i was nonzero
while (*q) *p++ = *q++;
*p = '\0'
答案 3 :(得分:1)
您已经有两个答案指向使您丢弃堆的特定错误。假设这是 家庭作业 或其他一些形式的练习(否则我们都会因为你自己写的字符串类而大喊大叫),这里还有一些要咀嚼的东西为你:
char* p = str; // new data
而不是char* new_data = str;
您可以只编写//do frgl
,而不是do_frgl();
,而不是一大块代码。如果函数是内联的,那么对于生成的代码没有任何区别,但对代码的读者来说有很多不同。 std
转储到全局命名空间 中的所有内容。 That's not a good idea at all.我会避免像瘟疫那样包括你的标题。 Str::Str(const char*)
构造函数为同一个字符串调用std::strlen()
两次
应用程序代码 应 尽快 , 库代码 < / strong>,另一方面,如果您不知道它结束的应用程序,则应尽可能 。你正在编写库代码。 size()
会员功能是否会返回 负值 ?如果没有,为什么它是有符号整数? Str s1, s2; s1=s2
?Str str("abc"); std::cout<<str[1];
(如果有人遇到此问题可以考虑更多提示,请随意扩展此内容。)