C ++ 예외 처리

Oct 21 2020

예외가 발생하고 검토가 필요합니다.

주요 기능은 메모리 할당을 여러 번 시도한 다음 상위 수준에서 예외를 throw합니다.

#include <iostream>
#include <memory>

struct MiserlinessClass{
    char  *   pMemory;
    int       memory_len;
    const int max_size = 10;
    MiserlinessClass(int len){
        if (len>max_size){
            std::cout<<"What a lavish lifestyle! Get out of my face! \n";
            std::bad_alloc exception;
            throw exception;
        }
        pMemory = (char *)malloc(len*sizeof(char));
        memory_len = len;
    }
};


int main(int argc, char** argv){
    int len = (argc==2)? strtol(argv[1],NULL,10): 5;

    std::unique_ptr<MiserlinessClass> objPtr;

    bool allocated = false;
    const int max_cnt = 5;
    int cnt = 0;
    while (!allocated){
        try{
            std::cout<<"Trying to allocate "<<len<<" chars...\n";
            objPtr.reset(new MiserlinessClass(len));
            allocated = true;
        } catch (std::bad_alloc &e){
            len = len >> 1;
            cnt++;
            if (cnt==max_cnt){
                std::cout<<"I give up \n";
                throw  e;
            }
        }
    } 
    std::cout<< "Allocated " << objPtr->memory_len << " chars \n";  
    return 0;
}

그리고 다음은 3 개의 다른 실행 결과입니다.

1-첫 번째 시도에서 메모리를 할당합니다.

2-몇 번 시도하고 사용 가능한 할당 및 T

3-N 번의 시도 후 실패하고 상위 수준으로 예외 발생

---------------------------------------
$ make; ./01_exception_pointer 3 make: Nothing to be done for 'all'. Trying to allocate 3 chars... Allocated 3 chars --------------------------------------- $ make; ./01_exception_pointer 64
make: Nothing to be done for 'all'.
Trying to allocate 64 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 32 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 16 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 8 chars...
Allocated 8 chars 
---------------------------------------
$ make; ./01_exception_pointer 1023
make: Nothing to be done for 'all'.
Trying to allocate 1023 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 511 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 255 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 127 chars...
What a lavish lifestyle! Get out of my face! 
Trying to allocate 63 chars...
What a lavish lifestyle! Get out of my face! 
I give up 
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

내 질문은

  1. 값을 반환하는 것과 예외를 던지는 것의 형식적인 차이를 알고 있지만, 당황하고 예외를 던질 때인 지 아니면 프로그램이 침착하게 유지하고 오류 코드를 반환해야하는지 여전히 느낄 수 없습니다.

  2. bad_alloc 예외를 처리하는 적절한 방법입니까?

  3. 추가 의견 및 제안 사항 :)

답변

3 MartinYork Oct 22 2020 at 00:56

값을 반환하는 것과 예외를 던지는 것의 형식적인 차이를 알고 있지만, 당황하고 예외를 던질 때인 지 아니면 프로그램이 침착하게 유지하고 오류 코드를 반환해야하는지 여전히 느낄 수 없습니다.

간단한 경험 법칙.

  • 로컬에서 문제를 해결할 수없는 경우 예외를 발생시킵니다.
    로컬에 충분한 메모리가없는 문제를 해결할 수 없으므로 예외를 throw하십시오. 이렇게하면 스택이 메모리를 해제하고이 문제를 해결할 수있는 지점에 도달 할 수 있습니다 (또는 응용 프로그램이 종료되지 않을 경우). 중지 할 수있는 예는 독립 작업을 생성 할 때 이러한 작업 중 하나가 실패하는 경우입니다. 모든 작업이 실패한다는 의미는 아닙니다. 이 작업이 실패했다는 사실을 기록하여 예외가 사용한 모든 메모리를 해제하고 다음 작업을 시도합니다.

  • 오류 코드는 인터페이스 경계를 ​​넘어서는 안됩니다.
    오류 코드를 확인하면 좋습니다. 간단한 메커니즘으로 코드를 복잡하게 만들지 않고 정보를 한두 수준 뒤로 전달할 수 있습니다. 따라서 라이브러리를 작성하고 내부적으로 괜찮은 오류 코드를 사용하는 경우 (잘하고 모든 오류 코드를 확인하기 때문에). 그러나 라이브러리 사용자를 신뢰할 수 없으므로 오류가 라이브러리 외부로 전파 될 경우 예외를 사용하여 사용자가 명시 적으로 가져 오도록 강제합니다.

  • 사용자 입력 및 스트림. 예외를 사용하지 마십시오. 사용자 입력은 항상 오류가 발생하기 쉬우 며 사용자 입력을 처리하는 코드에는 많은 유효성 검사가 필요합니다 (올바르게 수행 된 경우). 그들은 이것을 알고 있습니다. 따라서 스트림 작업에서 단순히 스트림을 불량으로 설정하십시오.

bad_alloc 예외를 처리하는 적절한 방법이 있습니까?

앱 상단으로 전파되도록합니다. 사용자에게 문제가 발생했음을 알리기 위해 무언가를 기록합니다. 이것이 독립적 인 작업 인 경우 작업을 삭제하고 다음 작업을 시작합니다. 이것이 실행의 정상적인 부분이라면 응용 프로그램을 종료하십시오.

추가 의견 및 제안 사항 :)

  • C ++ 코드에서 malloc()/ free()를 사용하지 마십시오 .
  • 모든 새로운 항목에 대해 삭제해야합니다.
  • (새로 만들기 / 삭제 일치를 돕기 위해) make_unique()보다는 사용 하는 것을 선호합니다 new.

코드 검토 :

// Owned pointers are a bad idea.
struct MiserlinessClass{
    char  *   pMemory;          // This is an owned pointer.
                                // At lot of extra work needs to be done here
                                // You need to look up the rule of three/five

            std::bad_alloc exception;
            throw exception;

간단하게 작성하기 :

             throw std::bad_alloc;

이것은 bad_alloc 상황이 아닙니다.

        if (len>max_size){
            std::cout<<"What a lavish lifestyle! Get out of my face! \n";
            std::bad_alloc exception;
            throw exception;
        }

bad_alloc은 메모리 부족으로 인해 시스템이 메모리를 할당하지 못했음을 의미합니다.

사용해야합니다 : std::range_error입력 매개 변수가 범위를 벗어났습니다.


사용하지 마십시오 malloc사용을 new여기에.

        pMemory = (char *)malloc(len*sizeof(char));

        // Better:
        pMemory = new char[len];

참고 : 여기서도 3 개의 규칙을 구현해야합니다.


참고 : 예, 위의 예외가 발생해야합니다. 사용자가 잘못된 개체를 만드는 것을 허용하지 않으려 고합니다. 애플리케이션이 프로덕션으로 허용되기 전에이 문제를 수정해야하므로 예외와 함께 단위 테스트가 실패하도록하는 것이 올바른 솔루션입니다.


클래스에 소멸자가 없습니다 MiserlinessClass. 따라서 할당 된 메모리 malloc()가 누출됩니다. 더 빨리 메모리 부족을 강제합니다.

3의 법칙을 참조하십시오.


참고 : 이렇게하면 이전 개체가 해제됩니다. 그러나 새 개체가 성공적으로 생성 된 경우에만 가능합니다. 따라서 많은 메모리가 할당됩니다. 그런 다음 두 배로 할당하려고합니다. 해당 할당이 작동하면 이전 메모리를 해제합니다.

            objPtr.reset(new MiserlinessClass(len));

선언을 옮길 것입니다.

   std::unique_ptr<MiserlinessClass> objPtr;

try 블록에 할당을 수행하십시오.

    try{
        std::cout<<"Trying to allocate "<<len<<" chars...\n";
        std::unique_ptr<MiserlinessClass> objPtr = std::make_unique<MiserlinessClass>(len);
        allocated = true;
    } catch (std::bad_alloc &e){
    ...

지금. 메모리가 할당 된 다음 시도가 끝날 때 해제됩니다. 다음에 루프를 돌면서 마지막 시도에서 모든 것이 정리되었으므로 할당 할 깨끗한 저장소가 있음을 알고 있습니다.

한 단계 더 나아가 간단히 unique_ptr.

    try{
        std::cout<<"Trying to allocate "<<len<<" chars...\n";
        MiserlinessClass       objPtr(len);
        allocated = true;
    } catch (std::bad_alloc &e){
    ...

2를 곱하면 안됩니다.

            len = len >> 1;

아니면 2로 나누는 것입니다. 어느 쪽이든 의도가 명확하지 않습니다. 의도를 명확하게 표현하는 코드를 사용하십시오.


예외가없는 경우에만 인쇄합니다.

    std::cout<< "Allocated " << objPtr->memory_len << " chars \n";