클래스에 대한 질문입니다. (두번째 부모 클래스의 주소 얻는 법?)
안녕하세요,
프로그래밍 초보입니다. 아는게 없다보니 질문 제목도 잘 못정하겠네요.
하고 싶은 일은,
기본적인 자료 구조 형태에 따라 A, B,C등의 클래스들을 만들었습니다.
이들 중 가장 기본은 A이고 다른 것들은 A로부터 파생해서 만들었습니다.
이 녀석들 각각은 상황에 따라 1,2,3차원에서 펑터인데,
이들 펑터들의 추상 클래스들을
class F1 { double EvalF1(double); },
class F2 { double EvalF2(double, double); }
이런 식의 인터페이스들을 가지게 만들었습니다.
그래서 이 추상 클래스 F1,F2,..들을
위 자료 구조와 조합해서
AF1, AF2, AF3, BF1, BF2, BF3, ... 이런 식의 최종 클래스들을 만들었습니다.
예: class BF2 : public B, public F2 {double EvalF2(x,y) { return ... ;} }.
여기에서 어떤 인스턴스 *obj가 있는데
이게 F2를 상속받은건 아는데 베이스 클래스가 A인지, B인지, C인지는 모르는 상황일때,
이 녀석의 F2::EvalF2()를 어떻게 호출할 지를 알고 싶습니다.
질문을 정리해보면,
어떤 오브젝트의 포인터 obj가 있고,
이 obj가 AF2, BF2, CF2 이런것들 중 하나인건 아는데 이들 중 어느것인지는 모릅니다.
(물론 꼭 알려면 알 수는 있는데 코딩이 복잡해지고 비효율적이 됩니다.)
이럴때 F2의 멥버함수를 호출할 수 있는 방법을 찾고 있습니다.
제가 해 본 방법은
((F2*) obj)->EvalF2(x,y);
이렇게 하면 컴파일은 잘 되는데 실행시 에러가 납니다.
억지로 할 수 있는 방법 하나는 class A 정의할 때
EvalF1(), EvalF2(),..., 이런 인터페이스들을 죄다 class A에 집어 넣어 놓고,
즉
class A {
double EvalF1(double);
double EvalF2(double, double);
...
}
((A*) obj)->EvalF2(x, y);
이렇게 하면 잘 되기는 합니다.
근데 이 방법은, 예를 들어, 1차원 펑터도 2차원, 3차원 펑터 인터페이스들을 다 가지고 있게 되어서
뭔가 좀 불만스럽고요.
제 문제에 대해 조언해 주시면 감사하겠습니다.
> 질문을 정리해보면, 어떤 오브젝트의 포인터
> 질문을 정리해보면, 어떤 오브젝트의 포인터 obj가 있고,이 obj가 AF2, BF2, CF2 이런것들 중 하나인건 아는데 이들 중 어느것인지는 모릅니다.
> ((F2*) obj)->EvalF2(x,y);
> 이렇게 하면 컴파일은 잘 되는데 실행시 에러가 납니다.
글쎄요, 에러가 나는 정확한 원인을 모르겠군요. 소스를 올려 보심이...
> 여기에서 어떤 인스턴스 *obj가 있는데 이게 F2를 상속받은건 아는데 베이스 클래스가 A인지, B인지, C인지는 모르는 상황일때, 이 녀석의 F2::EvalF2()를 어떻게 호출할 지를 알고 싶습니다.
VMT(virtual method table) pointer의 갯수는 하나의 object에 여러개가 존재할 수 있습니다. 예를 들어 A, B, C, F1, F2 가 모두 virtual function을 단 하나라도 가지고 있다면 AF2, BF2, CF2 식의 클래스는 2개의 VMT pointer를 가지게 됩니다.
AF2 : A VMT를 가리키는 pointer, F2 VMT를 가리키는 pointer (2개)
BF2 : B VMT를 가리키는 pointer, F2 VMT를 가리키는 pointer (2개)
CF2 : C VMT를 가리키는 pointer, F2 VMT를 가리키는 pointer (2개)
어느 클래스가 F2에서 상속을 받았다면 F2 class VMT를 가리키고 있는 pointer가 object 내부에 위치하게 되며 이 pointer는 A, B, C와 관련된 VMT랑은 전혀 상관이 없습니다.
www.gilgil.net
답변 고맙습니다. 제 코드는 다른 툴킷 라이브러리
답변 고맙습니다. 제 코드는 다른 툴킷 라이브러리 위에서 돌아가고 또 방대해서 어떻게 올려야 할 지 모르겠습니다.
조금 더 구체적으로 제 상황을 예를 들어 설명드리면,
class F2 {
...
double EvalF2(double x, double y) = 0;
...
}
class BF2 : public B, public F2 {
...
double EvalF2(double x, double y);
...
}
가 있고,
BF2 *b2= new BF2();
..
// b2의 멤버 변수들 입력 루틴.
// b2를 어떤 컨테이너 클래스에 집어 넣고, 나중 다시 필요시 다시 불러 오는 루틴 ...
container->Add(b2);
...
// 제 경우 class A는 제 프로젝트에서 삼라만상의 부모 클래스, 즉 CObject와 비슷한 녀석이기 때문에
// 다음과 같이 할 수 있습니다.
A *obj = (A*) container->At(n);
// 여기서 아래와 같은 라인을 쓸 수 있다는 뜻인가요 ?
((F2*) obj)->EvalF2(x,y);
제 경우에는 위처럼 해 봤더니
실행시 프로그램이 죽더군요.
제가 VMT에 대해서는 전혀 모르는데
BF2 *obj = (BF2*) (어떤것)
이런 경우는 물론 필요한 VMT를 잘 찾겠지만
위처럼
A *obj = (A*) container->At(n);
이렇게 되어 있다면,
실제로 obj가 BF2의 인스턴스라고 하더라도
F2의 VMT를 잘 찾을지가 (아님 어떻게 찾아야 하는지가) 제가 궁금한 점입니다.
형변환을 했으니 잘 찾을 수 있습니다. 다만
형변환을 했으니 잘 찾을 수 있습니다. 다만 컨테이너에서 꺼낸 인스턴스가 F2의 자식이 아니라면 크래시가 나겠네요.
그 인스턴스가 F2의 자식임은 프로그램에서
그 인스턴스가 F2의 자식임은 프로그램에서 확인했습니다.
사용된 툴킷 라이브러리가 어떤게 어떤 클래스로부터 상속되었는지를 확인해 주는 루틴이 있어서요.
그리고 좀 찾아보니 제 질문에 대한 답도 그 툴킷 라이브러리가 제공하는 것 같기도 한데
아직은 잘 모르겠습니다. 공부를 좀 더 해봐야 알것 같구요,
혹시 dynamic_cast가 제 문제에 대한 답이 될 수는 없는가요 ?
실은 이 dynamic_cast를 공부해 본 적도 사용해 본 적도 없는데요,
왠지
((F2*) obj)->EvalF2(x,y);
이 라인을
F2 *f2= dynamic_cast(obj);
if (f2) f2->EvalF2(x,y);
else printf("ERROR");
이렇게 하면 될 듯도 싶어서요.
정확한 상황은 모르겠는데 C++ 에서는 일단 (A*)
정확한 상황은 모르겠는데
C++ 에서는 일단 (A*) obj 스타일 캐스팅은 primitive (integer 같은)를 제외하고는 잊어버리셔야 합니다.
C-style casting 이 컴파일이 되는 이유는, 타입 체크를 안하기 때문입니다 (static_cast, const_cast, reinterpret_cast 중 되는 걸 그냥 하는데 이 셋 모두 딱히 체크를 하지 않습니다.)
C++ 에는 4가지 종류의 casting 이 있는데 dynamic_cast, static_cast, const_cast, reinterpret_cast 입니다.
3번째는 가끔 쓸일이 있고
4번째는 거의 쓰면 안되고
2번째는 뭘 하는지 정확하게 알면 쓰고
1번째는 안전하지만, 확실한 상황이 아니면 casting 이 되지 않습니다.
그 "확실" 한 상황은 딱 2가지가 있는데
Derived -> Base 인 경우 (upcasting), 아니면
Base* a = new Derived;
Derived* b = dynamic_cast(a)
단 Base 가 polymorphic 이어야 합니다. (C++ 기준으로는 virtual function 이 있어야 합니다.)
즉 더 일반적인 상위 타입으로 일반화하거나, 아니면 애초에 Derived 지만 polymorphic 하게 쓰기 위해 Base pointer 안에 들어가 있는 경우여야 합니다.
지금 상황이 결과적으로는 A* -> F2* casting 인데,
일단 "A 는 F2다" (A 가 F2 의 subtype) 은 아닌 것 같고
F2 가 A 의 subtype 인가요? 즉 F2 는 A 다 라고 말하는 게 논리적으로 말이 되는 상황인가요?
만약 그렇다면
C style casting (A*) 를 다 지우시고 (특히 A *obj = (A*) container->At(n); 에서 (A*) 가 필요한가요?)
dynamic_cast 나 static_cast 를 시도해 보시고,
만약 논리적으로 말이 안되면, 아마 multiple inheritance 가 아니라 코드가 좀 길어져도 composite 스타일로 작성하셔야 될 것 같습니다.
dynamic_cast 에서 dynamic_cast
dynamic_cast 에서 dynamic_cast 입니다. 왜인지 모르겠는데 빠졌네요.
댓글 달기