클래스에 대한 질문입니다. (두번째 부모 클래스의 주소 얻는 법?)

parkon의 이미지

안녕하세요,

프로그래밍 초보입니다. 아는게 없다보니 질문 제목도 잘 못정하겠네요.

하고 싶은 일은,
기본적인 자료 구조 형태에 따라 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차원 펑터 인터페이스들을 다 가지고 있게 되어서
뭔가 좀 불만스럽고요.

제 문제에 대해 조언해 주시면 감사하겠습니다.

gilgil의 이미지

> 질문을 정리해보면, 어떤 오브젝트의 포인터 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랑은 전혀 상관이 없습니다.

parkon의 이미지

답변 고맙습니다. 제 코드는 다른 툴킷 라이브러리 위에서 돌아가고 또 방대해서 어떻게 올려야 할 지 모르겠습니다.
조금 더 구체적으로 제 상황을 예를 들어 설명드리면,

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의 자식이 아니라면 크래시가 나겠네요.

parkon의 이미지

그 인스턴스가 F2의 자식임은 프로그램에서 확인했습니다.
사용된 툴킷 라이브러리가 어떤게 어떤 클래스로부터 상속되었는지를 확인해 주는 루틴이 있어서요.

그리고 좀 찾아보니 제 질문에 대한 답도 그 툴킷 라이브러리가 제공하는 것 같기도 한데
아직은 잘 모르겠습니다. 공부를 좀 더 해봐야 알것 같구요,

혹시 dynamic_cast가 제 문제에 대한 답이 될 수는 없는가요 ?
실은 이 dynamic_cast를 공부해 본 적도 사용해 본 적도 없는데요,
왠지
((F2*) obj)->EvalF2(x,y);
이 라인을

F2 *f2= dynamic_cast(obj);
if (f2) f2->EvalF2(x,y);
else printf("ERROR");

이렇게 하면 될 듯도 싶어서요.

sblade의 이미지

정확한 상황은 모르겠는데
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 스타일로 작성하셔야 될 것 같습니다.

sblade의 이미지

dynamic_cast 에서 dynamic_cast 입니다. 왜인지 모르겠는데 빠졌네요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.