2016-12-22 1 views
4

다음 코드는 컴파일을 할당하지만, 런타임시 오류를 전달 된 연속 메모리 블록을 삭제할 수 없습니다 :소멸자가

0x7ff87bc03200 
0x7ff87bc03200 
0x7ff87bc03200 
0x10f9bcf64 
123 
abc 
a.out(883,0x7fff7ee3d000) malloc: *** error for object 0x10f9bcf64: 
pointer being freed was not allocated 
*** set a breakpoint in malloc_error_break to debug 
Abort trap: 6 

I :

# include <iostream> 
# include <string.h> 

class A { 
public: 
    A() {} 
    A (int id, char * t_name) { 
    _id = id ; 
    name = new char [ strlen (t_name) + 1 ] ; 
    strcpy (name, t_name) ; 
    } 

    char *name ; 
    int _id ; 
    ~A() { delete [] name ;} 
} ; 

int main() { 
    A a (1, "123") ; 
    A b ; 
    b = a ; 

    std::cout << static_cast < const void * > (a.name) << std::endl ; 
    std::cout << static_cast < const void * > (b.name) << std::endl ; 

    b.name = "abc" ; // b.name is directed to a different memory block 
    std::cout << static_cast < const void * > (a.name) << std::endl ; 
    std::cout << static_cast < const void * > (b.name) << std::endl ; 
    std::cout << a.name << std::endl ; 
    std::cout << b.name << std::endl ; 

    return 0 ; 
} 

이 같은 출력 그것이 말하는 이유를 이해하지 않습니다

0x10f9bcf64 : 포인터

할당되지 않은 해제되고

b.name은 분명히 0x10f9bcf64으로 지정되어 있으므로 더 이상 a과 중복되지 않습니다!

이 문제가 어떻게 해결 될 수 있는지 궁금하십니까? 고맙습니다 !

+4

의 규칙의 놀라운 세계에있어 3의 규칙. –

+1

'b.name'은'new''d 메모리가 아니므로'delete'd가 될 수 없습니다 –

답변

1

우선 생성자 선언은

A (int id, const char * t_name) 
      ^^^^^^ 

처럼 보일 것입니다.

기본 복사본 할당 연산자는 개체의 데이터 멤버를 구성원별로 복사합니다. 이 문 다음에 코드와 관련하여

b = a ; 

개체에는 동일한 동적 할당 메모리를 가리키는 포인터가 두 개 있습니다. 따라서 삭제 연산자는 동일한 메모리 주소에 대해 두 번 호출됩니다.

명시 적으로 클래스의 복사 할당 연산자와 복사 생성자를 작성해야합니다.

예를 들어, 복사 할당 연산자는

b.name = "abc" 

도 잘못 다음과 같은 방법

A & operator = (const A &a) 
{ 
    if (this != &a) 
    { 
     char *tmp = new char[ std::strlen(a.name) + 1 ]; 
     std::strcpy(tmp, a.name); 

     delete [] this->name; 

     this->_id = a._id; 
     this->name = tmp; 
    } 

    return *this; 
} 

에게이 문을 볼 수있다. 문자열 리터럴에는 정적 저장 기간이 있습니다. 따라서 메모리를 삭제할 수 없습니다.

+0

할당 연산자가 구현되어야 할뿐만 아니라 생성자 복사 및 작업 이동 (cf. 3/5/0 규칙).'std :: string'을 사용하여 메모리를 수동으로 관리한다는 생각이 떨어지면 않는 한. 또한이 오류는'b = a; '가 아니라'b.name = "abc";'(그러나'b.name = "abc";가없는 경우,'b = a; 오류) – wasthishelpful

+0

@wasthishelpful 우선 당신은 완전히 틀리다. 이 오류의 원인은 복사 할당 연산자가 없기 때문입니다. 둘째로 나는 복사 생성자가 명시 적으로 정의되어야한다는 것을 내 대답에서 지적했다. 그리고 세 번째로 그러한 단순한 프로그램을위한 이동 특수 기능을 정의 할 필요가 없습니다. –

+0

죄송 합니다만, 실제로 복사 생성자를 언급하셨습니다. ^^ OP의 실제 사용법을 알지 못하므로 IMHO에서는 이동 특수 기능을 정의 할 필요가 없다고 말할 수 없습니다. 우리는 여기에 언급했는데, 나는 충분히 행복하다. ^^ 나는 당신과 의견이 다르다 : OP의 코드에서 삭제 된 메모리는 **가 아니라'a'에서 복사 된 포인터이다. 'b.name'을 재 할당하지 않았 더라면 OP에있는 오류의 원인에 대해 당신과 동의했을 것입니다. OP를 넘어서는, 실종 된 복사본 할당 연산자는 실제로 오류이므로, 나는 당신과 어쨌든 동의한다. – wasthishelpful

0

포인터를 인스턴스 a에서 인스턴스 b로 복사하고 있습니다.

인스턴스 a의 소멸자가 실행되면 메모리를 삭제합니다.

인스턴스 b의 소멸자가 실행될 때 동일한 메모리를 다시 삭제합니다.

이 클래스에 복사 및 할당 생성자를 추가해야합니다. (그리고 당신은 C++ 11 사용하는 경우 이동 생성자)가 말했다 왜 이해가 안

0

을 : b.name 분명히 0x10f9bcf64 및 에 관한 것이다 이후 "0x10f9bcf64 할당되지 않은 해제되고 포인터" 더 이상 a와 중복되지 않습니다!

b의 소멸자가 정적 문자열에서 delete []을 호출하기 때문에.

이 문제가 어떻게 해결 될 수 있는지 궁금합니다.

당신은 같은 복사 생성자, 뭔가 정의한다 :

A::A(const A& lhs) { 
    _id = lhs.id; 
    name = new char [ strlen (lhs.name) + 1 ] ; 
    strcpy (name, lhs.name) ; 
} 

을 또한 name_idprivate했다. 당신은 클래스와 문자열 리터럴의 대상이 일정한 배열의 형식을 초기화하는 문자열 리터럴을 사용하고 있기 때문에

3

먼저 Rule of 3/5/0을 읽어야합니다. 귀하의 성명 : 클래스 A가 구성원으로 포인터를 가지고 있기 때문에, (당신이 즉, C++ 11 이상, ++ 현대 C를 사용하는 경우 5)

b = a; 

3의 규칙을 위반하는 것입니다.

다음으로,이 사항이 고려하는 경우 :

b.name = "abc"; 

현재는 new로 할당하지 않은 정적 문자 배열에 영향을 미치는 것입니다. 그래서 소멸자를 삭제하려고 할 때 :

~A() { delete [] name ;} 

delete[]에 대한 호출이 오류가 발생합니다.

간단한 해결책은 std::stringname를 선언하는 것입니다 :

class A { 
public: 
    A() {} 
    A (int id, const std::string& t_name) { 
    _id = id ; 
    name = t_name; 
    } 

    std::string name ; 
    int _id ; 
} ; 

int main() { 
    A a (1, "123") ; 
    A b ; 
    b = a ; 

    std::cout << static_cast < const void * > (&a.name) << std::endl ; 
    std::cout << static_cast < const void * > (&b.name) << std::endl ; 

    b.name = "abc" ; // b.name is directed to a different memory block 
    std::cout << static_cast < const void * > (&a.name) << std::endl ; 
    std::cout << static_cast < const void * > (&b.name) << std::endl ; 
    std::cout << a.name << std::endl ; 
    std::cout << b.name << std::endl ; 

    return 0 ; 
} 

std::string 때문에 당신을 위해 메모리를 관리, 당신은 당신은을 위반 한 다시 0