我已经实现了一个类字符串,类似于std :: string one。 调用析构函数时遇到问题:字段长度具有字段中分配的字符长度。 这是班级:
class indexException:public std::exception
{
public:
virtual const char* what()
{
return "Index is either too long, or negative";
}
};
class string
{
public:
static const unsigned int length_max=100;
string(const char* field=NULL)
{
if(field!=NULL)
{
const unsigned int length=strlen(field);
this->field=new char[length+1];
this->length=length;
for(unsigned int i=0;i<=length;i++)
this->field[i]=field[i];
}
else
{
this->field=NULL;
length=0;
}
}
string(string& str)
{
string(str.field);
}
~string()
{
if(length>0)
delete field;
}
char& operator[] (int i) const throw()
{
try
{
if(i<0 || i>=(int)length)
throw indexException();
}
catch(indexException& e)
{
std::cerr << e.what() << std::endl;
}
return field[i];
}
string& operator=(const char* field)
{
const unsigned int length=strlen(field);
if(this->length>0)
delete this->field;
this->field=new char[length];
this->length=length;
for(unsigned int i=0;i<length;i++)
this->field[i]=field[i];
return *this;
}
string& operator= (const string& str)
{
if(this!=&str)
*this=str.field;
return *this;
}
operator char* ()
{
return field;
}
friend std::ostream& operator<< (std::ostream& out, string& str);
friend std::istream& operator>> (std::istream& in, string& str);
public:
unsigned int length;
char* field;
};
std::ostream& operator<<(std::ostream& out, string& str)
{
out << str.field;
return out;
}
std::istream& operator>> (std::istream& in, string& str)
{
char temp[string::length_max];
in >> temp;
str=temp;
return in;
}
如果我使用赋值运算符,则不会导致分段错误。 但它直接造成它。 我解释一下:
int main(int argc,char** argv)
{
string str="hi";
string str2=str;
return 0;
}
在赋值运算符重载中设置断点,我意识到赋值运算符不会导致分段错误。 问题出在后面,当从主要退出时。 如果我删除析构函数我没有得到这个分段错误,但我知道为什么我会遇到这个问题。
编辑:我已经明白问题出在哪里了。 我按照你的建议,但它仍然是分段错误。 但现在它不再在析构函数方法上崩溃,而是在赋值运算符重载:
string& operator=(const char* field)
{
unsigned int length=0;
if(field!=NULL)
length=strlen(field);
else
field="";
if(this->length>0)
delete[] this->field;
this->field=new char[length+1];
this->length=length;
strcpy(this->field,field);
return *this;
}
问题是当我删除this-&gt;字段时,调试器停在那里。 分段错误的一个例子:
string str("hi");
string str2=str;
这会导致分段错误。我想这是因为str2没有初始化,并且length有一个未定义的值。 如果我这样做:
string str("hi");
string str2;
str2=str;
没有任何分段错误。为什么? 我以为这叫:
string str2;
还调用构造函数,还是“=”运算符具有优先权? 怎么解决这个问题?
PS:我也改变了其他的东西,比如复制构造函数。 完整代码在这里: http://pastebin.com/ubRgaVr8
解决:我按照接受的回复中的建议更改了复制构造函数:
string(const string& str)
{
length=str.length;
field=new char[str.length+1];
memcpy(field,str.field,length+1);
}
答案 0 :(得分:3)
您的复制构造函数不会初始化对象。
string(string& str)
{
string(str.field); // Does nothing
}
string(str.field)
创建一个未命名的 string
并立即抛弃它。
不使用不同的构造函数初始化此对象。
由于您的对象现在只包含随机性,因此当您尝试销毁它时会发生不好的事情。
要确保事情已初始化,请创建一个私有成员函数
void initializeFromChars(const char* cString);
完成工作并在构造函数和赋值运算符中使用它。
答案 1 :(得分:2)
使用
分配内存后field = new char[length+1];
您应该删除它:
delete [] field;
而且你没有检查你的分配是否成功。
另一个被认为是良好做法的事情是在删除后将field
设置为NULL
,以便它不会被删除两次(如果你开始提供类),例如:
~string(){
delete [] field;
// field = NULL;
}
注意:根据Dietmar Kühl设置field=NULL
不是一个好习惯(看看评论)并选择你的方式,这里有一个具体的问题:{ {3}}。
注意2 :Is it worth setting pointers to NULL in a destructor?指出delete [] field
如果指针为NULL
则无效,且不需要整个条件。
比string& operator=(const char* field)
中你可能想要分配length + 1
并迭代它(包括终止NULL
)。
我不喜欢你的string& operator= (const string& str)
,你已经缓存了关于字符串长度的信息,你使用的是strlen()
,而不是手工复制char的字符。
您的复制构造函数看起来也很糟糕......您应该“复制”手动分配并逐字节复制到它。或者更确切地说,像fromCString(const char *)
一样构建受保护的函数,并在构造函数和赋值运算符中使用它。
如果这些没有帮助请求评论以获得更多帮助。
答案 2 :(得分:2)
您的析构函数使用delete
时应使用delete[]
。
答案 3 :(得分:2)
编辑:废弃我之前的回答,因为它不正确。
问题似乎是复制构造函数,您从源实例传递字段,好像它只是另一个空终止的char *,但它不是。
在前一个语句调用的char *赋值期间,不要复制空字符,而是使用内部长度字段,并仅复制那么多字节。
所以你的拷贝构造函数应该是:
string(string& str)
{
length = str.length;
field = new char[length];
memcpy(field, str.field, length);
}
或者,如果你想保留与null终止函数的兼容性,并确保为所有其他赋值/构造函数保留null,等等:
string(string& str)
{
length = str.length;
field = new char[length + 1];
memcpy(field, str.field, length + 1);
}
事实上,混合空终止,并且在整个班级中指定的长度字符串似乎让你感到困惑。
我将创建一个内部的,私有的,单一的处理方法,以及一组设置各种源类型的方法,并让构造函数,赋值运算符和析构函数使用它们。
这样,您只有一个地方可以进行任何给定的操作,而不是在相同的功能上处理许多微小的变化。例如:
private:
void internalSet(const char *source) {
if (source == NULL) {
length = 0;
field = NULL;
}else{
length = strlen(source);
field = new char[length];
memcpy(field, source, length);
}
}
void internalSet(const string &source) {
length = source.length;
if (length > 0) {
field = new char[length];
memcpy(field, source.field, length);
}else{
field = NULL;
}
}
void internalDispose() {
delete[] field;
}
public:
string() : field(NULL), length(0) {}
string(const string& source) { internalSet(source); }
string(const char *source) { internalSet(source); }
~string() { internalDispose(); }
string& operator=(const char *source) {
internalDispose();
internalSet(source);
return *this;
}
string& operator=(const string &source) {
internalDispose();
internalSet(source);
return *this;
}
void clear() {
internalDispose();
length = 0;
}