C++, 다중상속, dynamic_cast

wafe의 이미지

클래스 A가 있습니다. 그리고 인터페이스 흉내를 내려고 클래스 B를 만들어서 가상함수만 넣었습니다. 그리고 C는 A와 B를 상속 받아서 가상 함수를 구현했습니다.

class A {
public:
    void MethodA1();
};

class B {
public:
    virtual void MethodB1() = 0;
};

class C : public A, B {
   void MethodB1() { // blahblah  }
};

그 외에도 같은 형식으로 A,B를 상속받아 B의 가상 함수를 구현한 클래스들이 많습니다. D, E, F, ...

그리고 함수가 하나 있는데 이 함수가 A형 포인터를 받는데, 실제로는 항상 C, D, E, F 등등 상속된 클래스만 받습니다. 그런데 이 함수에서 내부에서 B의 포인터로 바꿀 일이 있습니다. B*형을 받는 다른 함수를 호출해야 하거든요. 대충 이런 식이지요.

func1(A* pA) {
    // 조건 1
    if(...) {
        B* pB = dynamic_cast<B*>(pa);
        func2(pB);
    }
    else {
         // 딴짓
    }
}

이 코드에서 dynamic_cast 가 실패하는 것이 정상인가요? 반환값이 NULL이 나와서 pB를 쓸 수가 없네요. 그리고 이 경우에 C 스타일 캐스팅을 사용하면 문제가 발생하지는 않을까요?

Testors의 이미지

wafe wrote:
클래스 A가 있습니다. 그리고 인터페이스 흉내를 내려고 클래스 B를 만들어서 가상함수만 넣었습니다. 그리고 C는 A와 B를 상속 받아서 가상 함수를 구현했습니다.
class A {
public:
    void MethodA1();
};

class B {
public:
    virtual void MethodB1() = 0;
};

class C : public A, B {
   void MethodB1() { // blahblah  }
};

그 외에도 같은 형식으로 A,B를 상속받아 B의 가상 함수를 구현한 클래스들이 많습니다. D, E, F, ...

그리고 함수가 하나 있는데 이 함수가 A형 포인터를 받는데, 실제로는 항상 C, D, E, F 등등 상속된 클래스만 받습니다. 그런데 이 함수에서 내부에서 B의 포인터로 바꿀 일이 있습니다. B*형을 받는 다른 함수를 호출해야 하거든요. 대충 이런 식이지요.

func1(A* pA) {
    // 조건 1
    if(...) {
        B* pB = dynamic_cast<B*>(pa);
        func2(pB);
    }
    else {
         // 딴짓
    }
}

이 코드에서 dynamic_cast 가 실패하는 것이 정상인가요? 반환값이 NULL이 나와서 pB를 쓸 수가 없네요. 그리고 이 경우에 C 스타일 캐스팅을 사용하면 문제가 발생하지는 않을까요?

A 와 B 타입은 아무 관계가 없습니다.
해서 상호간 캐스팅도 불가능합니다.
그래서 dynamic_cast 가 NULL 을 리턴할 것입니다.
C 스타일 캐스팅을 써서도 안됩니다.

A 포인터를 B 포인터로 바꾸기 위해서는
A 포인터를 C 와 같이 A 와 B 간의 관계를 가지고 있는 포인터로 바꾼 다음
C 포인터를 B 포인터로 바꾸는것이 바른 방법입니다.

참고로 가상테이블을 가진 클래스들의 다중상속시
실제 인스턴스는 동일하다 해도,
참조하는 타입에 따라 값이 달라집니다. (아래 예제 참고)
아래 예에서는 reinterpret_cast 를 사용하였지만,
만약 p1 포인터를 p2 로 변환시 C style cast 를 사용한다면
결과는 reinterpret_cast 로 변환한것과 같을 것입니다.

#include <stdio.h>

class Parent_1
{
public:
    virtual void DoSomething() = 0;
};

class Parent_2
{
};

class Child : public Parent_1, public Parent_2
{
public:
    void DoSomething()
    {
        printf( "child\n" );
    }
};

int main()
{
    Child *child = new Child;

    Parent_1 *p1 = static_cast< Parent_1* >( child );
    Parent_2 *p2 = static_cast< Parent_2* >( child );

    printf( "Child : 0x%08X\n", child );
    printf( "P1        : 0x%08X\n", p1 );
    printf( "P2        : 0x%08X\n", p2 );

    p1 = child;
    p2 = child;

    printf( "\n\n" );
    printf( "Child : 0x%08X\n", child );
    printf( "P1        : 0x%08X\n", p1 );
    printf( "P2        : 0x%08X\n", p2 );

    p1 = reinterpret_cast< Parent_1* >( child );
    p2 = reinterpret_cast< Parent_2* >( child );    // <- 이거 참조하면 뻑남

    printf( "\n\n" );
    printf( "Child : 0x%08X\n", child );
    printf( "P1        : 0x%08X\n", p1 );
    printf( "P2        : 0x%08X\n", p2 );     
}

..
결과는 아래와 같습니다.

Child : 0x00372ED0
P1    : 0x00372ED0
P2    : 0x00372ED4


Child : 0x00372ED0
P1    : 0x00372ED0
P2    : 0x00372ED4


Child : 0x00372ED0
P1    : 0x00372ED0
P2    : 0x00372ED0
doldori의 이미지

클래스 A와 B는 서로 관계가 없으므로 직접 dynamic_cast를 하면
항상 0이 나옵니다. 그러나 A*가 가리키는 개체가 C라는 것을 알고
있다면 안전하게 B*로 바꿀 수 있습니다.

A* pa = new C;
if (C* pc = dynamic_cast<C*>(pa))
{
    B* pb = pc;
    pb->MethodB1(); // pc->MethodB1()과 동일
}

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.