2010-07-19 7 views
4

간단한 클래스에서 new'ed 메모리에 대한 포인터를 감싸는 아주 바보 같은 실수를하고 있습니다. 내가 클래스의 인스턴스와 벡터를 채울 때new with C++ constructor

class Matrix 
{ 
    public: 
    Matrix(int w,int h) : width(w),height(h) 
    {   
     data = new unsigned char[width*height]; 
    } 

    ~Matrix() { delete data; } 

    Matrix& Matrix::operator=(const Matrix&p) 
    { 
      width = p.width; 
      height = p.height; 
      data= p.data; 
      return *this; 
    } 
    int width,height; 
    unsigned char *data; 
} 

......... 
// main code 
std::vector<Matrix> some_data; 

for (int i=0;i<N;i++) { 
    some_data.push_back(Matrix(100,100)); // all Matrix.data pointers are the same 
} 

는 내부 데이터 포인터는 모두 같은 메모리를 가리키는 끝?

+0

월요일에 일어나서 "나는 바보 야."라고 말하면서 고마워요 - –

답변

7

1. 복사 생성자가 누락되었습니다.

2. 할당 연산자는 포인터가 여러 개인 Matrix 개체 (포인터는 delete d)를 여러 번 남겨두기 때문에 포인터를 복사해서는 안됩니다. 대신 행렬의 전체 복사본을 만들어야합니다. @GMan이 효율적이고 예외 안전 인 operator= 함수를 작성하는 방법에 대한 철저한 설명을 제공하는 this question about the copy-and-swap idiom을 참조하십시오.

3. delete이 아니라 소멸자에서 delete[]을 사용해야합니다.

+0

그러나이 할당 연산자는 예외가 아닙니다. 만약'data = new unsigned char [width * height];'가 실패하면 이전 데이터는 없어지지만 새 데이터가이를 대체하지 않아 매트릭스가 더 이상 유효하지 않습니다. –

+0

고마워 내가 대신 ctor 할당 연산자를 썼다! 오랫동안 잊어 버린 Qt와 함께 일해 왔습니다. –

+0

예, 할당 연산자가 필요 없습니다. 손가락 메모리로 작성되었습니다.별로 좋지 않습니다! delete []는 여기 코드를 잘라내어 붙여 넣기 만했습니다. –

3

복사 생성자를 놓쳤습니다.

Matrix(const Matrix& other) : width(other.w),height(other.h) 
{   
    data = new unsigned char[width*height]; 
    std::copy(other.data, other.data + width*height, data); 
} 

편집 : 소멸자가 잘못되었습니다. delete 대신 delete[]을 사용해야합니다. 또한 할당 연산자는 이미 할당 된 배열의 주소를 복사하는 중이고 딥 복사를 수행하지 않습니다.

+0

그 할당 연산자도 잘못되었습니다. – sbi

+0

@sbi 나는 다른 사람들이 이미 정보를 추가하고 여기에 그것을 복제 할 필요가 없다고 생각했다. 변경됨. – pmr

7

복사 생성자, 복사 할당 연산자 또는 소멸자 중 하나를 쓸 때마다 세 가지를 모두 수행해야합니다. 이들은 빅 3이며 이전 규칙은 3의 규칙입니다.

지금 복사 생성자는 완전 복사를 수행하지 않습니다. The Big Three를 구현할 때마다 copy-and-swap idiom을 사용할 것을 권장합니다. * 그대로, operator=은 올바르지 않습니다.


아마도 그것은 학습 운동,하지만 당신은 항상 책임감 클래스에게 하나를 제공해야합니다. 지금 당장은 메모리 리소스를 관리하고 Matrix입니다. 리소스를 처리하는 하나의 클래스와 리소스를 사용하기 위해 해당 클래스를 사용하는 클래스를 갖도록이 클래스를 구분해야합니다.

유틸리티 클래스는 The Big Three를 구현해야하지만 유틸리티 클래스 덕분에 암시 적으로 생성 된 것들이 적절하게 처리되기 때문에 사용자 클래스는 실제로 구현하지 않아도됩니다.

물론이 클래스는 이미 std::vector으로 존재합니다.

+0

* 예, 뻔뻔한 셀프 플러그. – GManNickG

+0

나는 3.5의 법칙 이었지만, –

+0

@Martin : 그렇습니다. 그러나 이제 그들은 우세 참조를 발명했고 이동 및 이동 지정을 갖게되었지만 실제로는 4.25의 규칙이되었습니다. – sbi

1

누락 된 사본 아이콘이 이미 지적되었습니다. 문제를 해결할 때 여전히 중요한 문제가 있습니다. 할당 연산자가 얕은 복사를 수행하면 정의되지 않은 동작 (동일한 데이터를 두 번 삭제)이 발생합니다. 전체 복사본 (예 :새 공간 할당, 기존 내용을 새 공간에 복사) 또는 참조 카운팅과 같은 방법을 사용하여 마지막 참조가 삭제 된 경우 데이터가 한 번만 삭제되도록해야합니다.

편집 : 편집의 위험이 있으므로 게시 한 것은 기본적으로 자신의 글을 쓰는 대신 표준 컨테이너를 사용해야하는 포스터 - 아이입니다. 직사각형 행렬을 원하면 wrapper around a vector으로 작성하십시오.

1

new[]을 사용하고 있지만 delete[]을 사용하고 있지 않습니다. 그게 really bad idea입니다. 그것을 할당을 해제하려고합니다 둘 -

그리고 당신의 할당 연산자가 만드는 두 인스턴스 같은 할당 된 메모리를 참조하십시오! 아, 그리고 당신은 왼쪽 메모리의 이전 메모리 누설입니다.

그리고 예, 에는 복사 생성자가 누락되었습니다.입니다. 그게 Rule of Three입니다.

1

얕은이 벡터에 복사 된 후 파괴되는 Matrix(100,100)의 임시 파일을 만드는 중입니다. 그런 다음 다음 반복에서 다시 구성되고 다음 임시 오브젝트에 대해 동일한 메모리가 할당됩니다.

는이 문제를 해결하려면, 당신은 또한이 완료되면 매트릭스의 개체를 삭제하는 몇 가지 코드를 추가해야합니다

some_data.push_back(new Matrix(100,100)); 

.

편집 : 다른 답변에서 언급 한 내용도 수정하십시오. 그것은 중요합니다. 그러나 복사 생성자와 대입 연산자를 변경하여 딥 복사를 수행하면 벡터를 채울 때 객체를 '새롭게'하지 마십시오. 그렇지 않으면 메모리가 누출됩니다.

+0

나는 이것이 동일한 메모리가 할당되도록 도약하지 못했다. 포인터 만 복사하기 때문에 얕은 사본 만 필요합니다! 나는 dtor로 디버깅하지 않았기 때문에 dtor가 호출되는 것을 결코 생각하지 못했습니다. 왜냐하면 dtors는 실패 할 수 없습니다. 나는 다음에 내가 고용 할 때 이것을 면접 질문으로 사용할 것이라고 생각한다. –

+0

이것은 더 복잡한 Matrix 클래스의 빠른 스텁 일 뿐이므로 벡터에 새로운 기능을 추가 할 수 없습니다. 그러나 참조 계산 된 객체와 똑똑한 poitners 나를 게으른 만들었습니다, 나는이 물건에 대해 걱정 잊어 버렸습니다. –