[英]Destructor causing segmentation fault
我實現了一個類字符串,類似於std :: string。 調用析構函數時,我遇到了一個問題:字段長度具有在字段中分配的字符的長度。 這是課程:
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;
}
將斷點放入賦值運算符的重載中,我意識到分配運算符不會引起分段錯誤。 問題出在從main退出時。 如果刪除析構函數,我不會遇到此分段錯誤,但是我會知道為什么會出現此問題。
編輯:我知道哪里出了問題。 我遵循了您的建議,但仍然會導致細分錯誤。 但是現在,它不再在析構函數方法上崩潰,而是在賦值運算符重載上崩潰:
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-> field時,調試器在那里停止。 分段錯誤的示例:
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);
}
您的副本構造函數不會初始化該對象。
string(string& str)
{
string(str.field); // Does nothing
}
string(str.field)
創建一個未命名的 string
並立即將其丟棄。 它不使用不同的構造函數初始化這個對象。
由於您的對象現在僅由隨機性組成,因此當您嘗試破壞它時,會發生壞事。
為確保初始化,請設置私有成員函數
void initializeFromChars(const char* cString);
完成工作並在構造函數和賦值運算符中使用它。
一旦你分配了內存
field = new char[length+1];
您應該使用以下命令將其刪除:
delete [] field;
並且您不檢查分配是否成功。
被認為是良好做法的另一件事是,在刪除后將field
設置為NULL
,這樣就不會被刪除兩次(如果您開始提供類),例如:
~string(){
delete [] field;
// field = NULL;
}
注意 :根據DietmarKühl的設置,設置field=NULL
並不是一種好習慣(請看注釋)並選擇您的方式,這是關於此的具體問題: 是否值得在析構函數中將指針設置為NULL? 。
注2 : KerrekSB指出,如果指針為NULL
並且不需要整個條件,則delete [] field
將不執行任何操作。
比在string& operator=(const char* field)
您可能想要分配length + 1
並對其進行迭代(包括終止NULL
)。
而且我不喜歡您的string& operator= (const string& str)
,您已緩存了有關字符串長度的信息,並且您正在使用strlen()
而不是逐字符手動復制char。
您的復制構造函數看起來也很糟糕...您應該“復制”手動分配,然后逐字節復制到它。 或更確切地說,構建諸如fromCString(const char *)
類的受保護函數,並在構造函數和賦值運算符中使用它。
如果這些方法不起作用,請在評論中尋求更多幫助。
您的析構函數應使用delete
,而應使用delete[]
。
編輯:取消了我以前的答案,因為它是不正確的。
問題似乎出在復制構造函數上,您正在從源實例傳遞字段,就好像它只是另一個以null終止的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);
}
實際上,在整個類中,混合null終止和指定長度的字符串如此之多,似乎使您感到困惑。
我將創建一個內部的,私有的,單一處理方法以及一系列方法來設置各種源類型,並讓構造函數,賦值運算符和析構函數代替使用它們。
這樣,您只有一個地方可以進行任何給定的操作,而不是在同一功能上處理許多細微的變化。 例如:
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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.