2016-07-18 4 views
0

응용 프로그램의 경우 예외를 처리 할 특수 클래스 집합을 만들어야합니다. std :: exception에서 기본 클래스를 파생했습니다. 그러나 나는 다이아몬드 문제와 애매한 상속에 직면했다. 가상 상속을 사용해도 도움이되지 않았습니다. 다음 예제는 문제를 보여줍니다.모호한 가상 상속

#include <iostream> 

class Exception: 
    public virtual std::exception 
{ 
public: 
    Exception(): 
     std::exception("This is default") 
    {   
     std::cout << "This is Exception\n"; 
    } 
}; 

class ChildException: 
    public virtual std::runtime_error, 
    public Exception 

{ 
public: 
    ChildException(): 
     Exception(), 
     std::runtime_error("hello") 
    { 
     std::cout << "This is ChildException\n"; 
    } 
}; 
int main() 
{ 
    ChildException exc();  
    //std::cout << static_cast<std::exception> (exc).what() << std::endl; 
    //std::cout << static_cast<std::runtime_error> (exc).what() << std::endl;  
    getchar(); 
    return 0; 
} 

코드는 컴파일되지만 작동하지 않습니다. 이 문제를 어떻게 해결할 수 있습니까?

+1

* 코드는 컴파일되지만 작동하지 않습니다. * - 작동하지 않음으로써 무엇을 의미합니까? –

+4

'exc'는'ChildException'을 반환하는 함수로 선언되었습니다. – Brian

+4

그냥 알 수 없지만 생성자'std :: exception (const char * const &)'는 비표준 확장자입니다. Visual Studio에서는이 기능을 사용하지만 다른 주요 컴파일러가 있는지는 잘 모르겠습니다. 앞으로 다른 컴파일러를 사용한다면 당신을 물으려 고 돌아올 수 있기 때문에 나는 그것을 지적 할 것입니다. –

답변

0

여기 다이아몬드 문제를 해결하는 다른 해결책이 있습니다. 이 솔루션에서 기본 Exception 클래스는 std :: exception에서 파생되지 않습니다. 대신 std :: exception에 대한 포인터를 포함합니다. 또한 Exception을 포인터를 사용하여 std :: exception로 변환하는 두 개의 변환기 함수가 있습니다. 파생 클래스는 std :: runtime_error를 사용하여 포인터를 초기화합니다. 이 방법을 사용하면 모호성이 해결되고 ChildException 객체를 std :: exception 또는 std :: runtime_error로 포착 할 수 있습니다.

#include <iostream> 

class Exception 
{ 
public:  
    explicit Exception() 
    { 
     pSTDException = nullptr; 
    } 
    operator std::exception*() 
    { 
     return pSTDException; 
    } 
    operator std::exception() 
    { 
     return * pSTDException; 
    } 
    std::exception * pSTDException; 
}; 

class ChildException: 
    public std::runtime_error, 
    public Exception 

{ 
public: 
    ChildException():   
     std::runtime_error("This is ChildException") 
    {   
     this->pSTDException = static_cast<std::exception *>(static_cast<std::runtime_error *>(this));   
    }  
}; 
int main() 
{  
    try 
    {   
     throw ChildException(); 
    }  
    catch(std::exception & e) 
    { 
     std::cout << e.what(); 
    } 
    getchar(); 
    return 0; 
} 
3

나는 순간에 두 가지 문제를 볼 수 있습니다


1 :

:

바와 같이 코멘트에 Brian 지적

가장 성가신 구문 분석을,이 라인은 실제로 함수의 프로토 타입입니다
ChildException exc(); 

ChildExceptionexc은 기본 생성자를 호출하여 초기화되거나 012라는 함수로 읽을 수 있습니다.은 ChildException을 반환합니다. 정확한 이유는 확실하지 않지만 C++ 표준은이 상황에서 후자로 읽히도록 지시합니다.

  • 괄호를 제거 : 당신은 그냥 기본 생성자를 호출하는 경우, 당신은 단지 괄호없이 쓸 수

    는이 문제를 해결하는 방법은 세 가지가 있습니다. 그러나 이것은 항상 옵션이되는 것은 아니지만, 함수 호출로 얻은 값으로 직접 초기화를 사용하려고 할 때 가장 귀찮은 구문을 파헤쳐 버릴 수도 있습니다.

    ChildException exc; 
    
    // Most vexing parse: 
    ChildException ce; 
    
    ChildException ce2(ce); 
        // This is safe, it can't be read as a function prototype. 
    ChildException ce3(ChildException()); 
        // This will be parsed as a function with: 
        // Return type: ChildException 
        // Parameter: Function pointer of type "ChildException (*)()". 
    
  • 를 사용하여 복사 초기화 : 당신은 컴파일러가 복사 생략을 통해 멀리 최적화 할당 구문, 그것을 초기화 할 수 있습니다.

    ChildException exc = ChildException(); 
    

    이 작동하지만, 불필요하게 못생긴 외모, 당신은 이 생략 복사 수행 할 수있는 컴파일러가 발생하면 덜 효율적되는 위험을 실행합니다.

  • uniform initialisation 사용 : C++ 11부터 uniform initialisation *을 지원하는 컴파일러를 사용할 때 괄호 대신 중괄호를 사용하여 생성자 호출을 지정할 수 있습니다. 질문의 태그를 고려해 볼 때이 방법을 권장합니다.

    ChildException exc{}; 
    

    는 * [세 "큰"컴파일러 중, 균일 한 초기화는 연타 3.1에서 지원되거나 이후, GCC 4.6 이상 및 Visual Studio 2013 이상. GCC는 4.4 버전을 지원했지만 Visual Studio는 2012 CTP 버전을 지원했지만 이전 버전에서는 일부 상황에서 어려움이있었습니다. 나는 Clang의 초기 버전에 문제가 있는지 확실하지 않습니다.]


2 : 더 구체적으로

//std::cout << static_cast<std::exception> (exc).what() << std::endl; 
//std::cout << static_cast<std::runtime_error> (exc).what() << std::endl; 

또는, : 다이아몬드 문제

나는 당신이 문제가있는 코드가 두 주석 처리 된 줄이라고 가정 할 문제는 두 번째 줄이 제대로 작동하는 동안 해당 줄 중 첫 번째 줄에 "모호한 변환"오류가 발생한다는 것입니다. 이는 ChildException이 실제로는 두 개의std::exception 개의 기본 클래스를 가지며 각 클래스는 서로 분리되어 있기 때문입니다. 당신이 경우 귀하의 Exception 사실상 std::exception에서 상속 동안, std::runtime_error하지 않는 것을,

class ChildException size(28): 
    +--- 
    | +--- (base class Exception) 
0 | | {vbptr} 
    | +--- 
    +--- 
    +--- (virtual base runtime_error) 
    | +--- (base class exception) 
4 | | {vfptr} 
8 | | _Mywhat 
12 | | _Mydofree 
    | | <alignment member> (size=3) 
    | +--- 
    +--- 
    +--- (virtual base exception) 
16 | {vfptr} 
20 | _Mywhat 
24 | _Mydofree 
    | <alignment member> (size=3) 
    +--- 

참고 : 클래스의 레이아웃은 특별히 이런 일을 보인다. 이로 인해 std::exception의 기본 문자가 Exceptionstd::exception베이스와 별개이므로 ChildExceptionstd::exception으로 변환하려는 시도는 모호합니다. ChildException::Exception::exception 기본 또는 ChildException::runtime_error::exception 기본을 참조 할 수 있습니다. 가능한 경우 예외 클래스를 리팩터링하여 각 클래스가 하나의 std 예외 클래스에서 상속받을 수 있도록 제안합니다. 때문에 다이아몬드 문제로 인한 문제로

// Cast into std::exception through the base classes: 
std::cout << "As Exception: " 
      << static_cast<std::exception>(static_cast<Exception>(exc)).what() 
      << std::endl; 

std::cout << "As runtime_error: " 
      << static_cast<std::exception>(static_cast<std::runtime_error>(exc)).what() 
      << std::endl; 

이 권장되지 않지만, 필요한 경우 사용할 수 있습니다 : 이것이 가능하지 않다면, 당신은 기본 클래스 중 하나를 통해 캐스팅 할 수 있습니다. 이는 각각 std::exception에 액세스하기 때문입니다. 첫 번째 클래스는 클래스 레이아웃 끝에 virtual에 액세스하고 두 번째 클래스는 std::runtime_error 내부에 액세스합니다.