2016-07-27 6 views
1

그래서 내가 dynamic_cast는, C - 스타일 캐스트, static_cast의 차이점에 대해을 좀 더 배우려고 노력하고있어 내가 결정 작동 C 스타일 캐스트static_cast 사이의 차이점을 반영해야하는이 예제를 시도해보십시오.메소드 호출

class B 
{ 
public: 
    void hi() { cout << "hello" << endl; } 
}; 

class D: public B {}; 

class A {}; 

int main() 
{ 
    A* a = new A(); 
    B* b = (B*)a; 
    b->hi(); 
} 

그럼이 코드는이 C 스타일 캐스트 매우 잘못 나쁜 캐스트가 전혀 발견되지 않는다는 것을 반영해야한다. 부분적으로 그런 식으로 발생합니다. 나쁜 캐스트는 감지되지 않지만, 프로그램이 b->hi();에서 충돌하는 대신 화면에 "hello"라는 단어가 인쇄 될 때 놀랐습니다.

이제 왜 이런 일이 발생합니까? 어떤 객체가 인스턴스화 된 B 객체가 없을 때 그러한 메소드를 호출하는 데 사용 되었습니까? 저는 g ++을 사용하여 컴파일하고 있습니다.

+0

이와 같은 관련없는 유형 (C++에서 효과적으로'reinterpret_cast'는 무엇인가)으로 캐스팅하는 것은 정의되지 않은 동작입니다. 이는 구현이 원하는대로 수행 할 수 있음을 의미합니다. – Yuushi

+0

힌트 : 깨진 시계조차도 적절한 시간을 하루에 두 번 보여줍니다. –

+0

C 스타일의 캐스트는 프로그래머가 컴파일러에게 '나에게 B 객체'라고 알려주는 것과 같다. 컴파일러는 동의 할 수 밖에 없습니다. 실행될 때 발생하는 일은 정의되지 않습니다. 이것이 바로 C 스타일의 캐스트가 너무 위험한 이유입니다. –

답변

3

다른 사람들은 정의되지 않은 동작이라고 말했습니다.

왜 작동합니까? 아마도 함수 호출이 컴파일 타임 동안 (가상 함수가 아닌) 정적으로 링크되기 때문일 것입니다. 함수 B::hi()이 있으므로 호출됩니다. class B에 변수를 추가하고 함수 hi()에 사용하십시오. 그런 다음 화면에 문제 (쓰레기 값)이 표시됩니다

class B 
{ 
public: 
    void hi() { cout << "hello, my value is " << x << endl; } 

private: 
    int x = 5; 
}; 

그렇지 않으면 당신이 기능 hi() 가상 만들 수 있습니다.그런 기능은 실행 프로그램에서 동적으로 연결되어 즉시 충돌 :

class B 
{ 
public: 
    virtual void hi() { cout << "hello" << endl; } 
}; 
+1

표준에 따라 제안 사항 중 하나라도 충돌을 가져와야한다는 보장은 없습니다. – user2079303

+0

@ user2079303 사실입니다. 내 컴퓨터가 그러한 호출 후에 화성에 임무를 시작하거나 Terminator : Genisys를 시작할 것입니다. 그러나 그가 gcc를 사용한다면, 결과는 정확하게 나의 것과 같을 것입니다 : 첫 번째는 이상한 값이고 두 번째 예는 충돌합니다. – Lehu

1

이제 왜 이런 일이 발생합니까?

발생할 수 있습니다. 아무거나 일어날 수있다. 문제는 입니다. 정의되지 않음입니다.

예기치 않은 일이 일어난 사실은 UB가 왜 그렇게 위험한지를 잘 보여줍니다. 항상 충돌이 발생하면 처리하기가 훨씬 쉬울 것입니다. 이러한 방법

대부분의 경우를 호출하는 데 사용 된 어떤 객체

는, 컴파일러는 맹목적으로 당신을 신뢰하고, b이 (가하지 않는) 유형 B의 객체에 지점을 수행한다고 가정합니다. 가정이 사실 인 것처럼 지적 된 메모리를 사용할 것입니다. 멤버 함수는 개체에 속한 메모리에 액세스하지 않았으며 올바른 형식의 개체가있는 경우와 동일한 결과가 발생했습니다.

정의되지 않은 동작은 완전히 다를 수 있습니다. 프로그램을 다시 실행하려고 시도하면, 표준은 악마가 코에서 날아 가지 않는다고 보장하지 않습니다.

1

때문에 hi() 방법 자체의 구현이 유일한 작품, 그리고 정의되지 않은 동작라는 C++ 사양의 독특한 부분.

호환되지 않는 포인터 유형에 대한 C 스타일 캐스트를 사용하여 캐스팅하는 것은 정의되지 않은 동작입니다. 문자 그대로 모든 것이 발생할 수 있습니다.

이 경우 컴파일러는 사용자를 신뢰하기로 분명히 결정했으며 이 실제로 B의 인스턴스에 대한 유효한 포인터라고 믿기로 결정했습니다. 실제로 이것은 모든 C 스타일의 캐스트가 수행 할 것입니다. 런타임 동작을 포함하지 않기 때문입니다.

  • 그것은이다 그것은 B에 속하는 인스턴스 변수에 액세스하지 않습니다하지만 A (실제로는 전혀 인스턴스 변수에 액세스하지 않습니다) : 당신이 그것을에 hi()를 호출 할 때하기 때문에, 방법은 작동 이 메서드 호출 다음과 같은 잘못된 캐스트를 vtable에 b의이

따라서 작동을 호출하기에 고개를하지만, 거의 모든 사소하지 않은 경우에 할 필요가 없습니다 있도록 가상하지 초래 충돌이나 메모리 손상에서. 그리고 이런 종류의 동작에 의존 할 수는 없습니다. 정의되지 않음은 실행될 때마다 동일해야 함을 의미하지 않습니다. 컴파일러는이 코드를 사용하여 난수 생성기를 삽입하고 1을 생성하면 원래 Doom의 전체 복사본을 시작할 수 있습니다. 정의되지 않은 동작과 관련된 사항이 있으면 작동하지 않을 수도 있습니다. 내일은 작동하지 않을 수도 있고 그렇게 처리해야 할 수도 있습니다.