当我运行这个程序str.cpp + str.h + main.cpp时,我不断收到分段错误(核心转储),我不能在我的生活中找出导致它的原因。我已经尝试通过每行代码并替换变量,但仍然得到该错误。这是我的代码,我要添加到我们的教授给我们的文件中:
//str.cpp
const str& str::operator=(const str& s)
{
str a;
a._n = s.length();
a._buf = new char[a._n];
strcpy(a._buf, s._buf);
return s;
};
str operator+(const str& a, const str& b)
{
str c ;
c._n = a._n + b._n;
c._buf = new char[c._n];
strcpy(c._buf,a._buf);
strcat(c._buf ,b._buf);
//strcat(strcat(c._buf,""),b._buf);
return c;
}
//main.cpp
#include "str.h"
int main() {
str s1;
cout << s1 << endl;
str s2("Hello");
cout << s2 << endl;
str s3("World");
str s4 = s2 + " " + s3;
cout << s4 << endl;
str s5, s6;
cin >> s5 >> s6;
cout << s5 << ' ' << s6;
return 0;
}
输出:./ str&lt; - user
您好
Hello World
123 345&lt; - user
分段错误(核心转储)
我希望输出为: ./str&lt; - 用户
您好
Hello World
123 345&lt; - user
123 345
如果有人可以请求帮助那就太好了。我真的不知道还有什么可以改变/做什么
答案 0 :(得分:3)
问题的operator=
实现中出错的核心是它没有分配给目标对象。让我们分解发生的事情:
const str& str::operator=(const str& s)
{
str a; // make a temporary
a._n = s.length(); // assign source's length to temporary's length
a._buf = new char[a._n]; // assign a buffer to the temporary
strcpy(a._buf, s._buf); // assign source's data to temporary
return s; // return source
}; // temporary goes out of scope and is destroyed
由于所有内容都存储在临时a
而不是this
中,因此dest = source;
dest
之后会保持不变。当a
在函数结束时超出范围并被销毁时,所有已完成的工作都将丢失。
要解决此问题:
源的返回有点奇怪,但是一旦其余部分被修复,你可能永远不会注意到这些效果。通俗地说,您应该返回刚刚分配的对象。关于operator=
(以及所有常见的运算符重载)应该是什么样子的真正好的阅读可以在这里找到:What are the basic rules and idioms for operator overloading?它简单易行,所以我们会把它变成我们的在进入主赛事之前先进行修正。
str& str::operator=(const str& s)
{
str a;
a._n = s.length();
a._buf = new char[a._n];
strcpy(a._buf, s._buf);
return *this;
};
请注意,此功能的内容与sbi建议的内容完全不同。 sbi正在使用Copy and Swap方法来分配运算符。我们稍后会再讨论这个问题,因为它通常是一个很好的起点,而且往往是一个停止的好地方,因为它非常简单,几乎不可能出错。
创建一个临时的a
,会为您提供除预期目的地this
以外的其他内容,以便分配给我们,所以让我们摆脱a
并使用this
const str& str::operator=(const str& s)
{
this->_n = s.length();
this->_buf = new char[this->_n];
strcpy(this->_buf, s._buf);
return *this;
};
但我们不需要在任何地方明确说明this
,所以
const str& str::operator=(const str& s)
{
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf);
return *this;
};
现在已为目标对象分配了源中所有内容的副本,但如果目标中已有内容,该怎么办?哎呦。刚刚泄漏了它。假设空str
为nullptr
或提供有效分配,则此问题的解决方案很快:
const str& str::operator=(const str& s)
{
delete[] _buf;
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf);
return *this;
};
但是,如果你正在做一些像a = a
那样愚蠢的事情并将对象分配给自己呢?
const str& str::operator=(const str& s)
{
delete[] _buf; // same object so this deleted s._buf
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf); // and s._buf is gone, replaced by the new empty _buf
// bad stuff will happen here
return *this;
};
你可以简单地说,“这就是你,傻瓜。”继续在C ++标准库中有很多想法,所以你不会脱节,但如果你想要原谅
const str& str::operator=(const str& s)
{
if (this != &s)
{
delete[] _buf;
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf);
}
return *this;
};
现在我们有一些不会使我们难堪的事情。除非s.length()
不包含允许strcpy
正常运行的终止空值。传统的字符串长度函数不包括终止null,因此我们需要一个额外的字符来适应null终止符。顺便说一下,这是导致坠机的最可能原因。
const str& str::operator=(const str& s)
{
if (this != &s)
{
delete[] _buf;
_n = s.length();
_buf = new char[_n + 1]; // +1 for null terminator
strcpy(_buf, s._buf);
}
return *this;
};
请注意,在为_buf
分配缓冲区的任何地方都需要这个额外的字节。
返回复制和交换,排序。
您需要一个复制构造函数来实现复制和交换的复制部分,但是要观察The Rule of Three对象需要有一个复制构造函数。让我们来看看我们需要什么来实现它。
str::str(const str& s)
{
if (this != &s) // don't need because you have to be really stupid to copy yourself
{
delete[] _buf; // don't need because this is a new object. It can't have an
// existing allocation
_n = s.length();
_buf = new char[_n + 1];
strcpy(_buf, s._buf);
}
};
有可能
str a(a);
并构造一个对象作为其不完整构造的自我的副本。我真的不知道为什么这在C ++中是合法的。可能是因为防止它的规则会搞砸其他更重要的东西。这样做是一种史诗般的愚蠢。 a = a;
可能因为某些令人困惑的间接而发生,您实际编写的内容为a = b;
,但有些函数b
被初始化为对a
的引用。或许你正在使用指针并搞砸了。无论如何,它可能发生。 str a(a);
需要一些审议或拼写错误。无论哪种方式,它都是错误的,应该修复。我不建议尝试防止这种情况。
无论如何,我们可以将复制构造函数简化为
str::str(const str& s)
{
_n = s.length();
_buf = new char[_n + 1];
strcpy(_buf, s._buf);
};
删除我们不需要的东西。然后,通过Member Initializer List的魔力,我们可以将构造函数减少到
str::str(const str& s): _n(s._n), _buf(new char[_n + 1])
{
strcpy(_buf, s._buf);
};
有了这种美,你可以利用复制和交换
str& str::operator=(str s) // copy constructor does all the copying
{
std::swap(_n, s._n); // exchange guts with s
std::swap(_buf, s._buf);
return *this;
}; // destruction of s deletes this's old _buf
或者,如果您已经实现了自己的swap
函数来交换str
。
str& str::operator=(str s)
{
swap(*this, s);
return *this;
};
如果您稍后要对swap
进行排序,那么拥有str
函数非常方便。
Pheeew!现在让我们看看operator+
str operator+(const str& a, const str& b)
{
str c ;
c._n = a._n + b._n;
c._buf = new char[c._n]; // need a +1 here for the null terminator
strcpy(c._buf,a._buf);
strcat(c._buf ,b._buf);
//strcat(strcat(c._buf,""),b._buf);
return c;
}
这家伙还不错。需要一些额外的空间用于终结器(c._buf = new char[c._n+1];
),但在其他方面可以使用。你可以在这里停下来。
但是如果您还需要为作业实施operator+=
,那么您也可以使用惯用解决方案。 What are the basic rules and idioms for operator overloading?建议实施operator+=
并围绕它operator+
。这看起来像
str& str::operator+=(const str& b)
{
_n += b._n;
char * temp = new char[_n+1]; // can't use _buf yet. Need free it's memory and
// to copy from it
strcpy(temp, _buf);
strcat(temp, b._buf);
delete[] _buf; // now we can release and reuse _buf
_buf = temp;
return *this;
}
str operator+(str a, const str& b)
{
a+=b;
return a;
}
关于习语的快速说明:
一般来说,除非有令人信服的理由,否则应坚持惯用解决方案。它们可以立即被有经验的程序员识别,无需任何解释。这样可以节省代码审查的时间,并在fewer surprises when other people work on or with your code中编写文档和结果。最重要的是他们工作。它们可能无法像专业解决方案那样工作,例如复制和交换成语的开销可能会不必要地昂贵,但它们是一个很好的起点,直到它证明有更好的解决方案并且你有充分的理由利用其他解决方案。
答案 1 :(得分:1)
您需要更改
a._buf = new char[a._n];
到
a._buf = new char[a._n + 1];
答案 2 :(得分:-1)
如果没有str
的定义,就不可能确切地说出需要什么,但有一点是肯定的,你不能返回对局部变量的引用。这样的事情是必需的......
const str& str::operator=(const str& s)
{
this->_n = s.length();
this->buf = new char[s._n]; // Plus one?
strcpy(this->buf, s._buf);
return *this;
};
修改强>
这是为了Jive的好处 - 因为评论中的代码不能很好地出现
str s1("Wibble"); // s1 has memory
str &s2 = s1; // s2 is a reference to s1
s1 = s2;