2009-03-30 7 views
4

내 프로젝트에서 생성자의 초기화 목록에서 메서드가 호출되는 코드 조각을 발견했습니다.생성자의 초기화 목록에서 예외 처리

Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID()) 
    { 
    } 

Test2의 사용자가 NULL을 생성자에 전달할 가능성이 있음을 확인했습니다. 유효성 검사없이 포인터가 사용되므로 액세스 위반 가능성이 있습니다.

이렇게하면 생성자의 이니셜 라이저 목록에서 예외 처리를 조사하게됩니다. 나는이 기사에서 initializer 목록 내부에서 try를 사용할 수 있음을 발견했다.

//Test class stores the unique ID and returns the same with API getTestID 
class Test 
{ 
public: 

    Test(int nID):m_nID(nID){ 
    } 

    int getTestID() const 
    { 
      return m_nID; 
    } 
private: 
    int m_nID; 

}; 


class Test2 
{ 
public: 

    Test2(Test* pTest) 
     try :m_pTest(pTest), m_nDuplicateID(pTest->getTestID()) 
    { 
    } 
    catch (...) 
    { 
     cout<<"exception cought "<< endl; 
    } 

    void printDupID() 
    { 
     cout<<"Duplicate ID" << m_nDuplicateID << endl; 
    } 
private: 

    Test* m_pTest; 
    int m_nDuplicateID; 
}; 

int main(int argc, char* argv[]) 
{ 

    Test* pTest = new Test(10); 


    Test2 aTest2(pTest); 
    aTest2.printDupID(); 


    delete pTest; 

    return 0; 
} 

가이 코드는 VC6.0 컴파일 점점되지 않은 : 나는이 개념을 테스트하는 작은 테스트 프로그램을 썼습니다. VC 6.0에서 컴파일되도록 변경해야합니까?

또한 기사 중 하나에서 생성자의 이니셜 라이저 목록에서 try를 사용하면 엄격하게 C++ 표준을 확인하지 않는다는 것을 발견했습니다. 이 경우 생성자의 initializers 목록 (표준 처리 방법)에서 예외를 처리하는 방법은 무엇입니까?

감사합니다.

+0

VC6은 표준 C++ 호환이 아닌 것으로 악명이 높습니다. –

답변

8

우선, NULL 포인터 표준을 역 참조하면 C++이 예외가 throw된다는 것을 보장하지 않으므로이 경우에는 코드가 쓸모가 없습니다.

두 번째로 예외가 발생하면 예외 처리기는 무엇을할까요?

셋째, 생성자/함수 예외 블록은 시간 낭비로 널리 간주됩니다.이 http://www.gotw.ca/gotw/066.htm 및 허브 셔터의 GotW 사이트에있는 다른 기사를 살펴보십시오.

+1

생성자의 pTest-> getTestID()도 예외를 throw 할 수 있습니다. –

+1

당신은 허브의 기사를하지 않습니다 : 건설 예외 블록은 다른 예외를 재실행하거나 로깅과 같은 부작용을 위해서만 사용되어야합니다. 그러한 목적을 위해, 그들은 확실히 시간 낭비가 아닙니다. 그들은 그 이상을 수행 할 수 없습니다. –

+0

그래, 거기에 가면 생성자 예외 물건. 입력 인수를 확인하고 적절하게 선언하거나 던지십시오. –

4

this article에 따르면, 당신은 그냥 할 수 없어 보이는 점에서 VC++ 6.0

당신은 7.0 upgade해야하거나 대신 생성자 본문에 초기화를 할 것 중 하나.

+0

초기화 목록을 피할 수 없기 때문에 (예 : 참조 초기화) 업그레이드를 진행할 수 있습니다. –

11

C++ 기본 섹션 3분의 15

함수-시도 블록을 연관 핸들러-seqwith의 thector 이니셜, 존재하는 경우, 상기 함수 바디. 의 ctor-초기화 또는 전송이 예외와 동일한 방식으로 함수 시도 블록의 처리기 제어 함수 본체 실행 중에 초기화 식의 실행 동안 던져진 예외 try 블록 실행 중에 throw되어 다른 핸들러에 컨트롤을 전달합니다.

class C 
{ 
    int i; 
    double d; 
public: 
    C(int, double); 
}; 

C::C(int ii, double id) 
try : i(f(ii)), d(id) 
{ 
//constructor function body 
} catch (...) 
{ 
//handles exceptions thrown from the ctor-initializer 
//and from the constructor functionbody 
} 
+0

"Eric P의 답변에있는 기사는 표준에 있지 않다고 말합니다. 그런 점에서 VC 6.0이 왜 지원하지 않는지 궁금합니다. –

+0

"Visual C++ 6.0에서는 이것을 컴파일 할 수 없습니다. 엄격하게 C++ 표준을 확인하지는 않습니다." –

+0

감사합니다. 혼란스러워. –

1

사람들은 여전히 ​​VC6을 사용할 수 있습니까? VC6은 표준 준수 컴파일러가 아닙니다. 부탁을 들어 적어도 VS2005를 얻으십시오. VC6가 문제입니다. VS2008 express를 사용해보고 컴파일되는지 확인하십시오.

물론 다른 옵션은 구속해야하는 구성에 대한 참조를 취하는 것입니다.

+0

VC6에는 많은 수백만 라인의 모 놀리 식 C++ 응용 프로그램이있어 많은 개발자들이 최신 C++로 포팅 할 수 있으며 많은 회사에서는 전환을 원하지 않습니다. –

+0

@ Pete :이 회사들은 점근적인 성장을 이해하지 못합니다. 비용의 증가, 즉. 그러한 고대 코드를 유지하는 것은 저렴하지 않으며 저렴하지도 않습니다. –

+2

동의합니다.하지만 (항상 계약자로서) 변화를 만들기에 충분한 영향력을 항상받는 것은 아니며, 종종 다루어야 할 더 많은 문제가 있습니다. –

3

당신은 단지 PTR, 예를 확인하는 기능을 사용할 수 없습니다 :

template<typename P> 
P* checkPtr (P* p) 
{ 
    if (p == 0) 
     throw std::runtime_error ("Null pointer"); 
    return p; 
} 

class Test2 
{ 
public: 
    Test2 (Test* pTest) 
     : m_pTest (checkPtr (pTest)) 
    { 
    } 

    Test* m_pTest; 
}; 
0

우리가 PTR의 복사본을 저장하지 않으려면 또 다른 해결책 (동료 Google 직원)/shared_ptr :

class Foo::Pimpl 
{ 
public: 
    bool paramTest_; 

    Pimpl(ConstNodePtr root) 
    try : 
     paramTest_(root ? true : throw std::invalid_argument("Foo (pimpl) constructed from NULL node")), 
    ... 
{ 
    ... 
} catch (...) 
{ 
    throw; // rethrow 
} 
0

이미 많은 유용한 답변이 있지만 조금 더 추가하려고합니다. 아마도 도움이 될 것입니다.

먼저 언급 한 다른 항목과 마찬가지로 nullptr을 역 참조하거나 잘못된 포인터 (주소)를 사용하면 표준 C++에서 예외가 발생하지 않습니다. MSVC는 Structured Exception Handling을 통해 지원하지만 휴대용은 아닙니다. this answer에서 자세히 알아보십시오.

생성자의 try 블록은 예외를 표시하지 못하도록합니다. 다른 예외를 throw하지 않으면 어쨌든 전파됩니다. catch 절을 입력하면 클래스의 모든 구성원이 이미 소멸됩니다. 그래서 당신이 할 수있는 유일한 그럴듯한 일은 오류를 어떻게 든 기록하거나 일부 전역 변수를 변경하는 것입니다. 그래서 더 쓸모없는 것으로 간주됩니다. 초기 코드로

Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID()) 
    { 
    } 

당신이 null의 경우 일부 적절한 조치 단지 초기화 목록에 PTEST의 nullness 확인하고 수행 할 삼항 연산자를 사용할 수 있습니다 - 다만 m_nDuplicateID은 nullptr로 설정 또는 일부를 다른 값의 유형에 따라, 등, 다른 함수를 호출하고 형식을 반환 사용

Test2(Test* pTest): 
    m_pTest(pTest), 
    m_nDuplicateID(pTest ? pTest->getTestID() : /*some value or call*/) 
{ 
} 

당신은 훨씬 더 복잡한 실행 흐름을 만들기 위해 여러 가지 중첩 된 삼항 연산자를 사용할 수 있습니다.

코드가 완벽하지는 않지만 동일한 상황에서 누군가를 착용 할 수 있습니다. 클래스 멤버 m_pTest를 사용하여 m_nDuplicateID를 초기화 한 경우 클래스 선언의 클래스 멤버가 초기화 순서 목록에 나타나는 순서가 아닌 선언 순서에 따라 초기화되므로 클래스 선언의 멤버 순서에 따라 다릅니다. 따라서 문제가 될 수 있으며 구성원 초기화 순서 종속성을 피하는 것이 좋습니다.

class A 
{ 
    A(B* pTest); 
    int m_nDuplicateID; 
    B* m_pTest; 
}; 

A::A(B* pTest) : 
    m_pTest(pTest), 
    m_nDuplicateID(m_pTest->someMethod()) // here m_pTest isn't initialized yet, 
              // so access violation probably 
{ 
}