当前位置:C++技术网 > 资讯 > 关于重载+运算符实现两个字符串合并出现的bug

关于重载+运算符实现两个字符串合并出现的bug

更新时间:2017-07-12 22:44:24浏览次数:1+次

这道题是C++ primer plus第十二章第二道编程习题a

#include <iostream>

using namespace std;

class String
{
private:
char *str;
int len;
static int num_strings;
public:
static const int CINLIM = 80;
String(const char *s);
String(const String&);
String();
~String();
int length()const{return len;}
String& operator=(const String &);
String& operator=(const char *);
char &operator[](int i);
const char& operator[](int i)const;
friend ostream& operator<<(ostream& os,const String&st);
static int HowMany();
String& operator+(const String &st1);
};
int String::num_strings = 0;

int String::HowMany()
{
return num_strings;
}

String::String(const char *s)
{
len = strlen(s);
str = new char[len+1];
strcpy(str,s);
num_strings++;
}
String::String()
{
len = 4;
str = new char[1];
str[0] = ''''\0'''';
num_strings++;
}
String::String(const String& st)
{
num_strings++;
len = st.len;
str = new char[len+1];
strcpy(str,st.str);
}
String::~String()
{
--num_strings;
delete[] str;
}
String& String::operator=(const String&st)
{
if(this == &st)
return *this;
delete[] str;
len = st.len;
str = new char[len+1];
strcpy(str,st.str);
return *this;
}
String& String::operator=(const char *s)
{
delete [] str;
len = strlen(s);
str = new char[len + 1];
strcpy(str,s);
return *this;
}
char & String::operator[](int i)
{
return str[i];
}
const char& String::operator[](int i)const
{
return str[i];
}

ostream& operator<<(ostream& os,const String& st)
{
os << st.str;
return os;
}

String& String::operator+(const String &st1)
{
int i = 0,j = 0;
String s = str;
int len1 =  strlen(str);
int len2 = strlen(st1.str)+ 1;
delete [] str;
str = new char[len1+len2];
strcpy(str,s.str);
for(i = 0,j = len1 + i; i < len2 - 1; i++,j++)
{
str[j] = st1[i];
}
str[j] = ''''\0'''';
return *this;
}

int main()
{
String s("abcdef");
String s1("1234");
String s2 = s1 + s;
cout << s2 <<endl;

system("pause");
return 0;
}

问题在于String& String::operator+(const String &st1)这函数,运行后能正常打印s字符串,但是按任意键关闭后出现异常,不知怎么解决


C++技术网会员解答:

    感谢您对C++技术网的支持与信任。

    注:因为一些原因,在分析一次代码后,没有找到问题。不过,下次我不会直接帮你查找代码的问题了的。不过我会帮你分析,告诉你查找问题的方法。而且越找到最后,我预感越有可能是低级错误,结果证明确实如此。如果我每次都这样帮你们去找代码错误,那将是没有什么意义的。自己在写代码的时候,思路一定要清晰。

    今天晚上,本来是很累了的。但是你这个问题不解答,我也难以入睡。然后就看着代码,看了两个多小时,就这么看着。在重载+操作符那研究了好久,代码也没有什么错误。其他地方太琐碎,而且小小的函数,不至于会出错吧。然后呢,在复杂的重载那就是没有问题,就只好看其他地方了。然后就看构造函数、重载=操作符的代码。

    代码:

String s("abcdef");
String s1("1234");
String s2 = s1 + s;
    s对象构建时,len是6,执行的是构造函数:

String::String(const char *s);
    同理,s1对象构建时,len是4。在+操作符重载函数中,你使用的是直接将s字符串拼接在s1后面,这样也就是直接修改了s1的字符串数据。但是此时s1的len成员并没有因此而更新。然后就返回了s2对象的引用。当在初始化s2的时候,调用的构造函数是:

String::String(const String& st)
    然后你获取st字符串长度,即s1的字符串长度是通过s1的len成员,所以得到了4。然而此时的字符串长度是10。当你申请一个新内存的时候,只有5个字节。然后再将10个字符的字符串复制给了5个字节的内存的str,然后就发生了堆内存越界。此时因为堆内存还没有被释放,所以暂时检测不出来,没有报错。而在析构函数中释放堆内存的时候,就检测到了堆被破坏了。这也正是报错的图的意思。

    所以,你要么在+重载操作符函数内,就将s1的len同步更新(不建议,因为这样操作成员变量会变得混乱),要么就在构造函数中使用strlen获取长度。都可以解决这个问题。这是问题的原因和分析。

    在分析解决这个问题的时候,你一定要清楚程序的执行流程。你将构造函数和=赋值重载搞混淆了,否则你也不会没有用到赋值操作。赋值构造函数是构造函数,=赋值操作符重载只是重新定义了赋值的过程。这里说的是赋值,不是初始化时的赋值。初始化的赋值时赋值构造函数,赋值构造函数会使用匹配的构造函数来执行。

    另外说几点你的代码方面的其他问题:

1.代码书写不规范,成员变量和局部变量难以区分,让代码阅读变难。如果代码再复杂点,我就懒得看了。成员变量命名,可以在前面加上m_,这样好区分。

2.+操作符重载的逻辑有不妥的地方。我们一般实现的+都不会直接修改相加的两个字符串,而是创建一个新的字符串返回。你这个实现,直接破坏了原有的字符串,不是好的风格。

3.指针使用不规范。指针在释放之后,一定马上将指针值设置为空值。然后再释放内存的时候,一定要先检测指针是否为空,这样可以避免很多指针错误。

    另外给一个经验:

1.一般在一个问题找了很久都没有找到的时候,记得更换思路,从其他地方找。同时,要注意很可能会出现低级错误。因为你以为很容易出错的地方,检查很多遍也没有发现错误。而你忽略的地方才发生了真正的错误。

2.堆内存问题:堆内存就是动态分配出来的内存。如果发现堆内存被破坏,那就是内存越界了。有可能是复制时越界了,也有可能是写内存时越界了。读内存越界不会出现破坏,会报异常的内存访问,即非法的堆内存访问。

3.其实解决你这个问题,也就只需要按照它每一步,一步步的跟踪,然后检查每一个的正确性。在出现一种判断有多种方式实现的时候,要做到统一处理原则。你这里就要统一用strlen或者统一用len。否则不同步就出现问题了。而且,对于一个变量的状态维持,如len,要时刻清楚的是,它是随着内存发生变化而变化的,一旦内存大小变化了,要立即更新。