2010-12-30 6 views
0

고정 된 크기의 배열과 해당 배열의 인덱스로 구현 된 사용자 정의 FastStack 클래스가 있습니다.new 연산자가 기존 객체를 덮어 씁니다.

내 복사본 생성자에서 배열을 할당 한 다음 복사 배열의 각 개체를 새 배열에 할당합니다. 스택에있는 객체에는 refcounting이 있으므로 간단한 복사본보다는 할당이 사용됩니다.

배열을 할당 할 때 가끔 다른 스택의 배열 일부를 덮어 쓰는 것이 문제입니다. 예상 할 수 있듯이 이는 데이터가 참조 해제 될 때 최종 세그멘테이션 오류를 발생시킵니다.

class FastStack { 
private: 
    int m_size, m_ptr; 
    ObjectRef* m_stack; 

public: 
    FastStack(int size) : m_size(size), m_ptr(-1) { 
     m_stack = new ObjectRef[m_size]; 
    } 

    FastStack(const FastStack& copy) : m_size(copy.m_size), m_ptr(copy.m_ptr) { 
     long a = (long)copy.m_stack[0]; 

     m_stack = new ObjectRef[m_size]; 

     if ((long)copy.m_stack[0] != a) 
      fprintf(stderr, "\nWe have a serious problem!\n\n"); 

     for (int i = 0; i <= m_ptr; i++) 
      m_stack[i] = copy.m_stack[i]; 
    } 

    ~FastStack() { 
     delete[] m_stack; 
    } 
}; 

class ObjectRef { 
private: 
    DataObj* m_obj; 

public: 
    ObjectRef() : m_obj(0) { } 

    ObjectRef(DataObj* obj) : m_obj(obj) { 
     if (m_obj) m_obj->addRef(); 
    } 

    ObjectRef(const ObjectRef& obj) : m_obj(obj.m_obj) { 
     if (m_obj) m_obj->addRef(); 
    } 

    ~ObjectRef() { 
     if (m_obj) m_obj->delRef(); 
    } 

    ObjectRef& operator=(DataObj* obj) { 
     if (obj) obj->addRef(); 
     if (m_obj) m_obj->delRef(); 
     m_obj = obj; 
     return *this; 
    } 

    ObjectRef& operator=(const ObjectRef& obj) { 
     if (obj.m_obj) obj.m_obj->addRef(); 
     if (m_obj) m_obj->delRef(); 
     m_obj = obj.m_obj; 
     return *this; 
    } 
}; 

"우리에게는 심각한 문제가 있습니다!" segfault 직전에 gdb를 실행하면 new에 의해 생성 된 ObjectRef 중 하나가 다른 스택의 배열과 동일한 주소를가집니다.

나의 첫 번째 본능은 새로운 것이 이미 사용중인 메모리를 할당해서는 안된다는 것이지만 분명히 여기에있는 것처럼 보이며 나는 무엇을 할 수 있는지에 관해 완전한 손실을 느낍니다.

추가 : 나는 이런 일이 볼 수있는 시간에서 m_size = 2 m_ptr = 0

이 프로그램의 전체 코드는 https://github.com/dvpdiner2/pycdc에서 GitHub의에 있지만 매우 복잡하고 따라하기 어렵다.

+0

어떤 컴파일러 버전입니까? 타사 라이브러리를 사용하고 있습니까? 빌드에서'sizeof (long) == sizeof (ObjectRef *) '가 확실합니까? 메모리를 할당하는 다른 프로그램의 수천 (수백만)에 영향을주지 않는 버그를 'new operator'에서 발견했을 수 있습니다. 아니면 우리에게 보여주지 않는 다른 코드에서 포인터 또는 배열 경계 오류가있을 가능성이 있습니다 ... – Blastfurnace

+3

'FastStack :: operator ='가 누락되어'FastStack : : ~ Fastack'. 우리가 정말로 필요로하는 것은 실행할 때 문제를 보여주는 ** compilable example **입니다. ** 이와 같은 모든 코드 스 니펫과 마찬가지로 ** 충분한 코드를 놓치 셨습니다 ** 우리가 말할 수있는 것은 문제가 다른 곳에서 발생했다는 것입니다. –

+1

'new는 이미 사용중인 메모리를 할당해서는 안되지만 분명히 여기에있는 것처럼 보일 것입니다. ' 절대적인 트 와들. 코드에 버그가 있습니다 (새 것이 아닙니다). –

답변

1
이 0 m_ptr까지 반복 루프에서

:

for (int i = 0; i <= m_ptr; i++) 
    m_stack[i] = copy.m_stack[i]; 

하지만, 배열 m_stack은 (당신이 그들을 초기화 가정) m_size 요소가 포함되어 있습니다.

편집 : 게재 위치 new 연산자를 사용하는 경우 m_stackcopy.m_stack이 겹치는 유일한 방법입니다. 그러나 게시 된 출처에 따르면, 당신은 그것을하지 않았다.

+0

m_stack이 할당되면 ObjectRef 생성자가 호출되어 기본값으로 배열을 초기화합니다. 이렇게되면 m_size는 2이고 m_ptr은 0입니다. – dpogue

2
때때로

new 이미 다른 개체에 할당 된 메모리를 제공하는 것으로 알려져 그 좋은 C++ 코드를 발생하면

NO ... 다른 사람과 공유하지 않는 메모리를 가지고있는 희망의 할당을 반복해야합니다!

new은 프로그램이 버그입니다. 이 백 번을 칠판에 적는다.

new 당신이 (즉, 그것이 가리키는 된 객체가 해제 된에 일단 포인터를 역 참조, 한계 밖으로에 액세스하기 전에 나쁜 아무것도 한 경우에 다른 사람 하지만 부여 된 사용자 메모리를 제공하지 않습니다, 사용되는 무효화 된 반복자 및 다른 가능한 "UB"위반의 jillions) new은 특별 허가를 얻고 당신의 코에서 데몬을 만드는 것을 포함하여 원하는대로 할 수 있습니다.

귀하의 코드가 매우 용의자를 표시하지만, 나는 확실히 오류에 대한 아무것도 표시되지 않습니다 (너무 많은 코드를 정확하게 ObjRef를하고 그것을 선언하는 방법을 어떤 예를 들어, 숨겨진이다). 그러나 내가 본 것을 보면 좋은 C++ 실행 규칙 위반이 많기 때문에 (예 : 표시된 클래스에 생성자와 복사 생성자가 있지만 할당 연산자 나 소멸자가 없음) 새 할당에 도달하기 전에 잘못된 작업을 한 것으로 확신합니다. .

그러나 표시된 코드의 가장 큰 문제점은 평범한 표준 :: 벡터가 수행 할 부분 집합을 모방 한 반 백업 및 버그없는 시도처럼 보입니다. 그러나 문제가 무엇인지 정확히 말하면 더 많은 컨텍스트가 필요합니다 ... 이것은 잘못된 코드이지만 이 다른 코드가 작성된 방법에 따라 합법적 일 수 있습니다. 이 작은 코드라도 아무 것도 액세스 할 수있는 방법이 없으므로 변경되고 축소되었다는 것은 분명합니다. 친구와 데이터 멤버는 비공개입니다 (기본적으로 이러한 객체로는 아무 것도 할 수 없습니다).

1

할 일은 std::vector을 사용하여 메모리를 관리하고 명시 적으로 색인 (m_ptr)을 관리하는 것입니다. 이것은 모든 문제를 해결할 것입니다.

그러나 나는 진실로 코드에 무엇이 잘못되었는지를 알 수 없습니다. 최적화 도구라고 생각하고 신속한 패스를 취하면 acopy.m_stack[0]에 집중할 수 있습니다.

long a = (long)copy.m_stack[0]; 

m_stack = new ObjectRef[m_size]; 

if ((long)copy.m_stack[0] != a) 



long a = (long)copy.m_stack[0]; 

if ((long)copy.m_stack[0] != a) 


if (copy.m_stack[0] != copy.m_stack[0]) 
해당 조건이 사실이라면, 당신은 당신의 힙 손상이 있어야합니다

가능하지만 스택 가능성이 적습니다. 힙 손상은 명백히 전혀 관련이없는 코드에서 실수로 코드에 맞을 수 있습니다. 어느 쪽이든, 또는 당신은 스레드에 속이고 그것에 대해 우리에게 말하지 않습니다. (그리고 그것을 잘못하고있다).

물론 operator = 및 destructor와 같은 몇 가지 중요한 기능이 누락되었습니다.하지만 벡터를 사용하는 경우 구현해야 할 것은 없습니다.

+0

메모리 할당을 수동으로 관리하는 대신 std :: vector를 사용하는 것이 좋습니다. – bcsanches

+0

나는 그가 여러 스레드로 작업 중임을 의심스럽게 생각합니다. 특히 오류가 수시로 발생하기 때문에 ... – mmmmmmmm

0

가장 가능성이 큰 범죄자가 실제로 복사 생성자에 대한 입력 인 것처럼 보입니다.

그러나이 코드는 거의 유효성 검사를 수행하지 않으며 구축 방법은 버그가 발견되거나 길을 잃기 전까지는 오용 될 수 있다고합니다. 이런 식으로 작성된 클래스는 지금보고있는 것처럼 버그를 찾기 어렵게 만듭니다. 당신이 최소한해야

간단한 것들 : 1. 당신은 그냥 [0] 3. 확인이 m_stack를 사용하는 경우에도 배열로 전에 예외 2.에게 인덱스를 경계를 던지는 새로운 처리 복사 생성자에 대한 입력이 유효합니다. (일명 초기화 됨)

아, 이처럼 메모리 오류를 추적하는 가장 쉬운 방법은 값을 매기는 것입니다. 놀랍게도 메모리 문제를 신속하게 없애고, 심지어 그 목표가 아니라고 생각합니다.

또한 가장 간단한 재현 가능한 형태로 문제를 격리 한 코드 조각을 포함 시키면 디버깅이 매우 간단합니다.

0

당신이 게시 한 전체 코드를 대체 할 수

class FastStack { 
public: 

protected: 
    std::vector<boost::shared_ptr<DataObj> > m_Stack; 
}; 

을 (당신이 벡터가 메모리를 미리 할당 된 것을 확인합니다 생성자를 추가해야하는 경우).

누군가 다른 사람이 이미 잘 수행 한 수업을 구현하여 불필요한 작업을 수행 할 때 ("다른 사람"은이 경우 스마트 사용자, 대부분의 사용자보다 똑똑 함) 문제를 해결하는 것보다

std :: vector는 할당 된 배열보다 훨씬 더 좋은 컬렉션이 될 것이며 boost::shared_ptr (또는 tr1/shared_ptr 또는 std :: shared_ptr이 tr1/C++ 0x 호환 컴파일러를 사용하는 경우) 수행 중입니다. 정확히 ObjectRef 클래스가 수행하는 작업 (더 나은). 코드에서 DataObj에 자체 refcount가 포함 된 것처럼 나타나며 대신 shared_ptrs를 사용하면 스크랩 할 수 있습니다.

사용할 수있는 도구를 사용하십시오. 작성해야 할 코드가 적을수록 버그를 작성할 기회가 줄어 듭니다. 코드를 위의 내용으로 바꾸면 여전히 문제가 발생하므로 다른 곳에서 오류가 발생합니다.

0

코드가 여러 스레드에서 작동 할 수도 있습니까?

새로운 기능은 이미 할당 된 메모리를 반환하지 않으므로 검사 메시지를 얻는 유일한 방법은 다른 스레드가 복사 인스턴스에 무언가를하고 있다는 것입니다.

이 의혹은 때때로 발생하는 경우에만 발생합니다. 일반적으로 멀티 스레딩 버그입니다 (수시로 발생하는 특별한 타이밍이 필요하기 때문에).

관련 문제