C++ 在实现运算符+=

C++ 在实现运算符+=,c++,operator-overloading,operators,new-operator,delete-operator,C++,Operator Overloading,Operators,New Operator,Delete Operator,我已经想了好几个小时了,我已经不知所措了。如果有人能告诉我什么时候我做错了,我会非常感激 我编写了一个简单的类来模拟字符串的基本功能。该类的成员包括一个字符指针data(它指向动态创建的字符数组)和一个整数strSize(它保存字符串的长度,无终止符) 由于我使用了new和delete,我实现了复制构造函数和析构函数。当我尝试实现操作符+=时,问题就出现了。LHS对象正确地构建了新字符串-我甚至可以使用cout打印它-但是当我尝试在析构函数中释放数据指针时,问题就出现了:在析构函数尝试释放的数据

我已经想了好几个小时了,我已经不知所措了。如果有人能告诉我什么时候我做错了,我会非常感激

我编写了一个简单的类来模拟字符串的基本功能。该类的成员包括一个字符指针data(它指向动态创建的字符数组)和一个整数strSize(它保存字符串的长度,无终止符)

由于我使用了newdelete,我实现了复制构造函数和析构函数。当我尝试实现操作符+=时,问题就出现了。LHS对象正确地构建了新字符串-我甚至可以使用cout打印它-但是当我尝试在析构函数中释放数据指针时,问题就出现了:在析构函数尝试释放的数据数组指向的内存地址处,我得到了一个“在正常块之后检测到的堆损坏”

以下是我的完整课程和测试计划:

#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;
}
#包括
使用名称空间std;
//类来模拟字符串
类Str{
公众:
//默认构造函数
Str():数据(0),strSize(0){}
//字符串文本的构造函数
Str(常量字符*cp){
数据=新字符[strlen(cp)+1];
char*p=数据;
常量字符*q=cp;
while(*q)
*p++=*q++;
*p='\0';
strSize=strlen(cp);
}
Str和运算符+=(常量Str和rhs){
//创建新的动态内存以保存连接的字符串
char*str=新字符[strSize+rhs.strSize+1];
char*p=str;//新数据
char*i=data;//旧数据
const char*q=rhs.data;//要追加的数据
//将旧字符串追加到新动态内存中的新字符串
而(*p++=*i++);
p--;
而(*p++=*q++);
*p='\0';
//为数据分配新值并标准化
删除[]数据;
数据=str;
strSize+=rhs.strSize;
归还*这个;
}
//复制构造函数
Str(const Str&s)
{
数据=新字符[s.strSize+1];
char*p=数据;
char*q=s.数据;
while(*q)
*p++=*q++;
*p='\0';
strSize=s.strSize;
}
//析构函数
~Str(){delete[]数据;}
常量字符和运算符[](int i)常量{返回数据[i];}
int size()常量{return strize;}
私人:
字符*数据;
国际标准化;
};

ostream&operator下面的代码块使p指向数组旁边

while (*p++ = *q++) ;
*p = '\0';
在复制构造函数中使用的更好(安全)解决方案:

while (*q)
    *p++ = *q++;
*p = '\0';
while (*i) *p++ = *i++; //in these loops, you only increment if *i was nonzero
while (*q) *p++ = *q++;
*p = '\0'
使用类似于复制构造函数中使用的习惯用法:

while (*q)
    *p++ = *q++;
*p = '\0';
while (*i) *p++ = *i++; //in these loops, you only increment if *i was nonzero
while (*q) *p++ = *q++;
*p = '\0'

您已经有两个答案指向导致垃圾堆的特定错误。假设这是作业或其他形式的练习(否则我们都会因为你写自己的弦乐课而对你大喊大叫),这里还有一些事情需要你仔细考虑:

  • 如果您觉得需要<强>注释< /强>代码,请考虑使其<强>更具表现性< /强>。 例如,而不是
    char*p=str;//新数据
    ,您可以只写
    char*new_data=str
    您可以只编写
    do_frgl(),而不是
    //do frgl
    ,后面跟着一段代码。如果函数是内联的,它对结果代码没有影响,但对代码的读者有很大的影响
  • 每个人(包括你的头)都会从名称空间
    std
    中获取所有内容,并将其转储到全局名称空间中。我会避免像瘟疫一样包括你的头球
  • 构造函数应该在初始化器列表中初始化类的成员
  • 您的
    Str::Str(const char*)
    构造函数为同一字符串调用了
    std::strlen()
    两次。
    应用程序代码应尽可能快
    库代码,另一方面,如果您不知道它以哪个应用程序结尾,则应尽可能快
    。您正在编写库代码
  • size()
    成员函数是否会返回一个负值?如果不是,为什么它是有符号整数
  • 这个代码会发生什么:
    strs1,s2;s1=s2

  • 那么这个呢:
    Str(“abc”);std::cout这里已经有很多很好的答案,但作为解决这类问题的工具,它值得一试。如果您可以使用*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) % %g++-g-o test test.cpp %考研 ==2293==Memcheck,内存错误检测器 ==2293==2002-2009年版权(C)和GNU GPL'd,朱利安·苏厄德等人。 ==2293==使用Valgrind-3.5.0-Debian和LibVEX;使用-h重新运行以获取版权信息 ==2293==命令:./test ==2293== ==2293==大小为1的无效写入 ==2293==at 0x8048A9A:Str::operator+=(Str const&)(test.cpp:36) ==2293==0x8048882:main(test.cpp:82) ==2293==地址0x42bc0dc是分配大小为12的块后的0字节 ==2293==at 0x4025024:运算符新[](无符号整数)(vg_替换_malloc.c:258) ==2293==by 0x8048A35:Str::operator+=(Str const&)(test.cpp:26) ==2293==0x8048882:main(test.cpp:82) ==2293== 地狱世界! 你好 ==2293== ==2293==堆摘要: ==2293==在出口处使用:0个块中有0个字节 ==2293==总堆使用率:4个alloc,4个free,分配了31个字节 ==2293== ==2293==所有堆块都已释放--不可能存在泄漏 ==2293== ==2293==对于检测到的和抑制的错误计数,请使用:-v重新运行 ==2293==错误摘要:1个上下文中的1个错误(已抑制:6个中的17个) % 你可以看到,它精确地指出了其他答案的线条