2012-02-18 5 views
0

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-> 필드를 삭제하면되고, 디버거가 중지됩니다. 분할 고장 예 :

string str("hi"); 
string str2=str; 

이 분할 잘못이야 STR2가 초기화되지 않기 때문에 그것의 suppone 일으키고, 길이가 정의되지 않은 값을 갖는다. 내가 대신 할 경우 어떤 분할 fault.Why

string str("hi"); 
string str2; 
str2=str; 

없다

?

string str2; 

또한 생성자를 호출, 또는 WAS은 "="연산자 우선 순위를 가지고 있다는 것입니다 : 나는 호출하면 생각? 어떻게 해결할 수 있습니까?

PS : 나는 또한 복사 생성자와 같은 다른 일을 변경했습니다. 전체 코드는 여기에 있습니다 : http://pastebin.com/ubRgaVr8

가 해결 : 내가 복사 생성자를 변경 허용 응답에 제안 :

string(const string& str) 
    { 
     length=str.length; 
     field=new char[str.length+1]; 
     memcpy(field,str.field,length+1); 
    } 
+2

복사 생성자가 그처럼 작동하지 않습니다. 그것은'string (string const & rhs) : string (rhs.field) {}'이어야합니다. 그러나 이것은 C++ 11의 새로운 기능이며 아직 널리 지원되지 않습니다. –

+3

new []를 사용하여 할당하려는 경우 delete []를 사용하여 삭제하려고합니다. –

+0

나는 delete 대신에 delete []를 사용하고 Vyktor가 말했듯이 if 조건을 변경하려고 시도했다. 여전히 세분화 오류가 발생한다. –

답변

2

편집 : 잘못된 이전 나의 답변을 스크랩.

문제는 복사 생성자 인 것처럼 보입니다. 소스 인스턴스의 필드를 마치 null로 끝나는 char *와 같이 전달하지만 실제로는 그렇지 않습니다.

이전 문장에서 호출 한 char * 할당 중에 null 문자를 끝에 복사하지 않고 내부 길이 필드를 대신 사용하고 해당 바이트 만 복사합니다.

string(string& str) 
{ 
    length = str.length; 
    field = new char[length]; 
    memcpy(field, str.field, length); 
} 

또는, 당신은 널 (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; 
    } 
+0

이것은 매우 열악한 스타일입니다. 실제로 이니셜 라이저 목록을 사용해야합니다. –

+0

스타일이 좋지 않습니까? 그 스타일은 완전히 주관적입니다. 다음은 중괄호 앞에 개행이 있어야하는지에 대한 코멘트입니다. OP에서 아직 문제가 발생하지 않은 경우에 대비하여 질문에서 분명하지 않은 구문을 사용하지 마십시오. C++은 크고 복잡한 언어입니다. – jka6510

+0

arg 생성자가 없으면 C++는 매개 변수를 전달하지 않고 객체를 만들지 않습니다. 이 경우 arg 생성자를 추가하지 않으면 아무런 이점이 없습니다. 또한 코드는 포인터 또는 참조를 사용하지 않으므로 문자열 인스턴스가 힙에 할당되지 않으며 스택에 모두 할당됩니다. – MJD

2

당신은 당신은 그것을 삭제해야

field = new char[length+1]; 

으로 메모리를 할당하면 :

delete [] field; 

그리고 할당이 성공했는지 여부를 확인하고 있지 않습니다.

좋은 실례로 간주 또 다른 것은 (당신이 제공하는 수업을 시작하는 경우) 후 예를 들어, 그래서 두 번 삭제되지 않습니다 삭제 NULL- field을 설정하는 것입니다

:

~string(){ 
    delete [] field; 
    // field = NULL; 
} 

: Dietmar Kühl에 따라 설정 field=NULL 좋은 연습 (의견을 한번보세요)이 아니며 길을 선택하십시오. 구체적으로 여기에 대한 질문은 Is it worth setting pointers to NULL in a destructor?입니다.

주 2 : KerrekSB 포인터가 NULL이며, 전체 조건이 불필요한 경우 delete [] field는 아무것도 할 것이라고 지적했다.

string& operator=(const char* field)보다 큰 숫자를 입력하고 length + 1을 반복하고 싶을 수도 있습니다 (예 : NULL 종료 포함).

그리고 string& operator= (const string& str)이 마음에 들지 않습니다. 문자열 길이에 대한 정보가 캐시되어 있고 strlen()을 사용하고 있습니다. char에 의한 수동 복사 char보다.

복사 생성자도 좋지 않습니다 ... 수동 할당을 "복사"하고 바이트 단위로 복사해야합니다. 또는 fromCString(const char *)과 같은 보호 된 함수를 생성하고 생성자와 할당 연산자 모두에서 사용하십시오.

도움이 필요하지 않은 경우 의견을 물어보십시오.

+1

'field'를 NULL로 설정할 필요가 없습니다. 그것은 삭제되었거나, 사라 졌거나, 죽었습니다 (이것은, 아주 가깝습니다!). 소멸자가 사업을 마친 후에는이 필드가 다시 표시되지 않습니다. – wilhelmtell

+0

@wilhelmtell 미안하지만 * EX-PARROT *는 무엇입니까? 그리고 내 참조 : http://www.amazon.com/Teach-Yourself-21-Days/dp/0672310708 (이전 볼륨). – Vyktor

+0

@wilhelmtell 어쨌든 클래스 전달을 시작하면 실수로 같은 데이터를 두 번 삭제할 수 있거나 여러 속성이있는 경우 실수로 두 번 삭제하려고 할 수 있으므로 우수 사례로 간주됩니다. 좋은 연습으로 간주되지 않는지 확인하기위한 좋은 참고 자료가 필요합니다. – Vyktor

2

소멸자는 을 사용할 때 delete을 사용합니다.

3

복사 생성자가 개체를 초기화하지 않습니다.

string(string& str) 
{ 
    string(str.field); // Does nothing 
} 

string(str.field)string 이름을 만들고 바로 멀리 던진다. 아니요 다른 생성자를 사용하여이 개체를 초기화합니다.

개체가 이제는 임의성으로 만 구성되므로 개체를 파괴하려고 시도하면 문제가 발생합니다.

는 반드시 일이 초기화되어 있는지 확인하는 작업을 수행 개인 멤버 함수

void initializeFromChars(const char* cString); 

을하고 생성자와 할당 연산자를 사용합니다.

관련 문제