2011-01-02 5 views
5

저는 C++에서 다중 상속을 오랫동안 사용해 왔지만 요즘은 포인터 주소가 서브 클래스 중 하나로서 참조 할 때 포인터 주소가 다를 수 있다는 것을 알 수있었습니다. 생성자에 인쇄 할 때서브 클래스 캐스팅 및 포인터 주소 변경

class ClassA{ 
    public: 
     int x; 
     int y; 
     ClassA(){ 
      cout << "ClassA : " << (unsigned int)this << endl; 
     } 
    }; 

    class ClassC{ 
    public: 
     int cc; 
     int xx; 
     ClassC(){ 
      cout << "ClassC : " << (unsigned int)this << endl; 
     } 
    }; 

    class ClassB : public ClassC, public ClassA{ 
    public: 
     int z; 
     int v; 
     ClassB(){ 
      cout << "ClassB : " << (unsigned int)this << endl; 
     } 
    }; 


    int main(){ 

    ClassB * b = new ClassB(); 

    } 

클래스 A와 클래스 C는 서로 다른 주소를 가지고 : 나는 경우 예를 들어

.

그러나 나는 서로 다시 캐스팅 할 때, 그냥 자동적으로 작동합니다

ClassA * the_a = (ClassA*)b; 
cout << "The A, casted : " << (unsigned int)the_a << endl; 

ClassB * the_b = (ClassB*)the_a; 
cout << "The B, casted back : " << (unsigned int)the_b << endl; 

내가 이런 종류의 정보는 코드에서 컴파일러에 의해 유도 될 수 있다고 생각하지만,에 안전 이 모든 컴파일러에서 작동한다고 가정하십니까?

추가 질문 : 하위 클래스 위치의 순서를 강제로 지정할 수 있습니까? 예를 들어, 클래스 C가 하위 클래스 인 것처럼 classA가 먼저 있어야합니다 (본질적으로 동일한 포인터 위치 공유). 먼저 하위 클래스 선언에 먼저 넣어야합니까? 업데이트 주문을 강제로 할 수없는 것 같습니다. 수퍼 클래스 레벨에서 구조의 "루트"주소, 서브 클래스에 할당 된 주소의 시작을 찾을 수 있습니까? 예를 들어 ClassA에서 classB의 주소를 가져 오는 경우를 예로들 수 있습니다.

+0

_ "루트"_ 주소를 얻을 수 있다고 생각하지 않습니다. 나는 그것에 대해 별도의 질문을 할 것이다. 표준 AFAICT를 사용하면 기본 클래스 멤버 또는 하위 객체가 하위 메모리 주소에 배치되지 않을 수도 있습니다. 다시 말해, the_a가 the_b 또는 그와 비슷한 것보다 낮은 주소를 가지고 있다는 보장은 없습니다. –

답변

6

이것은 다중 상속을 완벽하게 표준으로 사용하며 모든 호환 컴파일러에서 작동합니다. 그러나 첫 번째 경우에는 명시 적 캐스트가 필요하지 않으며 'C 스타일 캐스트'는 두 번째 경우에 static_cast으로 대체 될 수 있습니다.

하위 클래스 위치의 순서를 강제로 지정할 수 있습니까?

아니요 : 레이아웃이 구현에 정의되어 있으며 코드가이 주문에 의존해서는 안됩니다.

+0

나는 본다. 그런 다음 수퍼 클래스 (예 : 클래스 A)가 전체 구조의 루트 주소 (클래스 A의 주소 인 경우 클래스의 주소 또는 클래스 B의 주소 인 경우 클래스 B의 주소)를 가져올 수있는 가능한 방법이 있습니까? 루트 주소에 의존하는 메모리 풀 imeplementation을 구축하고있었습니다. – kamziro

+0

@ kamziro : 사실, 나는 거기 있다고 생각하지 않는다. 그러나 나는 당신이 심지어 그것이 필요하다는 것에 놀랐다고해야만한다. – icecrime

+0

나는 본다. 하위 클래스로 내 메모리 풀을 구현하면 풀 주소로 자유 주소를 반환해야하고 기본 주소를 저장하지 않아도 4 바이트를 절약 할 수 있습니다. 뻔뻔스런 4 바이트 .. 만약 아무 것도 선택의 여지가 없다면 다음과 같이 추측 할 수 있습니다. – kamziro

3

예, 안전하다고 생각할 수 있습니다. C++의 포인터 유형 변환은 포인터를 기본에서 파생 변환으로 또는 반대로 변환하도록 올바르게 조정할 수 있습니다.

그렇다면 시스템을 너무 멀리 밀지 않도록주의해야합니다.

ClassB* b = new ClassB; 
ClassC* c = (ClassC*)(void*)b; 

이것은이 무효 *을 통해 깔때기 및하려면 C에서 캐스팅이 너무 포인터가 B 객체의 내부에 위치에 대한 정보는 나누기 때문에 예를 들어, 컴파일러는이 변환 권리를받지 않습니다 잃어버린.

스트레이트 캐스트가 작동하지 않는 또 다른 경우는 가상 상속입니다. 파생 클래스에서 가상베이스로 또는 그 반대로 캐스트하려는 경우 dynamic_cast 연산자를 사용하여 캐스트가 성공하는지 확인해야한다고 생각합니다.

+0

수정 해 주셔서 감사합니다! 이렇게 드물게 일어나서 나는 그것을 실험 할 기회가별로 없다. – templatetypedef

+0

가상의 상속에 대해 잘 부탁드립니다. 까다로운 주제입니다. 나는 당신이'dynamic_cast'를 사용해야 만한다는 것을 깨닫지 못했지만 여러분은 그렇게합니다. C++ 03 §5.2.9 5 단락과 8 단원을 참조하십시오.이 단락에서는 기본에서 파생 클래스로 캐스팅 할 때 가상이 아닌 상속을 명시 적으로 요구합니다. –

1

사실 가상 기반에 대한 순진한 구현은 파생 된 개체의 알려진 위치에 가상 기본 하위 개체에 대한 포인터를 배치하는 것입니다.

약간 비싼 가상 기반이 여러 개있는 경우 더 나은 표현 (내가 생각하는 마이크로 소프트에 의해 특허받은)은 자기 상대적 오프셋을 사용합니다.이들은 각 하위 객체에 대해 변하지 않으므로 (객체 주소에 의존하지 않음) 정적 메모리에 한 번 저장할 수 있으며 각 하위 객체에서 포인터 하나만 있으면됩니다.

이러한 데이터 구조가 없으면 크로스 캐스트가 작동하지 않습니다. B 하위 오브젝트가 보이지 않더라도 가상베이스 A의 가상베이스 A에서 가상베이스 A로 가상베이스 A로 크로스 캐스트 할 수 있습니다 (정확하게 리콜하면 두 개의베이스에만 공유 가상베이스 X가 있음). 그것은 꽤 털이 많은 네비게이션입니다. 네비게이션은 가장 많이 파생 된 클래스 (A와 B를 모두 볼 수 있음)의 모양 설명자를 통해 이동합니다.

더 털이 많은 것은 그렇게 표현 된 구조가 "표준의 법에 의해"건설 및 파괴 중에 동적으로 변경되어야한다는 것입니다. 이것이 복잡한 대상의 구성 및 파괴가 느린 이유입니다. 그러나 그것들이 만들어지면 메소드 호출, 심지어 교차 호출도 꽤 빠릅니다.

관련 문제