2016-06-26 3 views
-4

다음 코드 부분은 사용자 정의 데이터 형식이 String입니다.이 클래스의 객체는 문자 포인터 str (짧은 문자열)과 length을 저장합니다.C++에서 소멸자 호출

#include<iostream> 
#include<cstring> 
using namespace std; 
class String 
{ 
    char* str; 
    int length; 
public: 
    String(){}      //default constructor 
    String(const char* s) 
    { 
     length=strlen(s); 
     str=new char[length+1]; 
     strcpy(str,s); 
    } 
    void add(String a,String b) //function to concatenate strings 
    { 
     length=a.length+b.length; 
     str=new char[length+1]; 
     strcpy(str,a.str); 
     strcat(str,b.str); 
    } 
    void display() 
    { 
     cout<<str<<endl; 
    } 
    ~String()        //destructor 
    { 
     delete str; 
     cout<<"Destructor invoked!"; 
    } 
}; 
int main() 
{ 
    String s1; 
    String s2("Well done!"); 
    String s3("Boy"); 
    s1.add(s2,s3); 
    s1.display(); 
    s2.display(); 
    s3.display(); 
} 

출력 : 소멸자 invoked.Why이입니다 얻을 display 기능 이전에도 호출되는 것처럼

Destructor invoked!Destructor invoked!Well done!boy 
X!!; 
<!!; 
Destructor invoked!Destructor invoked!Destructor invoked! 
  • 이 나타 납니까? 소멸자 함수가 정의되지 않은 경우 (예상대로)

, 나는 다음과 같은 출력을 얻을 :

Well done!boy 
Well done! 
boy 
  • 왜 소멸자를 정의에 따라 예상치 못한 출력은?
+3

소멸자는 'add'인수가 소멸되기 때문에 호출됩니다. – MikeCAT

+4

그리고 귀하의 클래스는 3의 규칙을 준수하지 않습니다. 최소한 생성자 복사 및 대입 연산자를 복사해야합니다. –

+0

메모리 누수 * 및 * 이중 삭제가 모두 있습니다. 'add', 그리고 [three of rule은 무엇입니까?] (http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three)를보십시오. – juanchopanza

답변

1

이 기본 생성자

String(){} 

는 두 데이터 멤버가 기본 유형으로하고 있기 때문에 기본적인 유형은 자동 초기화를 제공하지 않습니다 아무것도를 초기화하지 않습니다.

따라서 결과 인스턴스를 사용하면 (데이터 멤버의) 불확정 값이 사용되므로 정의되지 않은 동작이 발생합니다.

수정 : 데이터 멤버를 초기화하십시오.


또한 복사 및 이동을 담당해야합니다. 두 번 피하기 위해 또한 정의되지 않은 동작입니다.

예제 코드 카피를 호출

s1.add(s2,s3); 

중 & hellip에 String 인스턴스; add이 값으로 인수를 취하기 때문입니다.

수정 : 적어도 복사 생성자를 정의하십시오.

소멸자

에게 해제

delete str; 

중 & hellip; 이 (가) 인 new[]을 사용한 할당과 일치하지 않습니다.

실제로 작동 할 수 있지만 정의되지 않은 동작이므로 모든 종류의 문제가 발생할 수 있습니다.

수정 : new[]으로 생성 된 내용에 delete[]을 사용합니다.


코드 것 여전히 누설 메모리 위에 고정 후. 간단한 수정은 연결 생성자를 정의하는 것일 수 있습니다. add을 사용하여 새 String 인스턴스를 만든 다음 swap 인스턴스를 만들고 현재 인스턴스의 상태를 만듭니다. 이 접근법은 자동으로 (거의 자동으로) 예외 안전을 얻는 이점이 있습니다.


일반 정보 :

  • 대신 <cstring>, 단지 <string.h>를 사용합니다. 하나의 장점은 동일한 이름의 C 헤더와 마찬가지로 전역 이름 공간에 이름을 배치 할 수 있다는 것입니다. 따라서 strcpy과 같은 수식어가없는 이름을 사용하면 컴파일러에 대한 우연한 의지 만이 아니라 이동이 가능합니다.

  • 대신 void add(String a,String b) 같이 값 잠재적으로 큰 객체를 전달 , 불필요한 복사를 방지하기 위해, void add(String const& a,String const& b) 같이 const에 참고로이를 전달한다.

  • i/o와 관련이없는 클래스에서 void display() 멤버 함수처럼 i/o를 우선적으로 사용하지 마십시오. 예를 들어, display() 함수는 그래픽 사용자 인터페이스에서 작동하지 않습니다.

1

는이 생성자를 복사해야합니다 void add(String a,String b); 그것은 참조로 문자열을 통과하는 것이 좋습니다 :

void add(const String &a, const String &b); 

또한 소멸자에서 삭제 []와 메모리를 해제해야합니다.

delete[] str; 

이제 작동합니다.

+0

Re "이제 작동 할 것입니다.", 아니요, 코드에는 복사 생성자를 추가 한 후에도 UB가 있습니다. –