[C++] 상속에서의 형변환 - 다운캐스팅

2024. 6. 27. 23:51·Programming Language(Sub)/C++

상속에서의 형변환 - 다운캐스팅(Downcasting)

  • 다운 캐스팅 : 부모 클래스에서 자식 클래스로 내려 보내는 형태의 캐스팅

    • static_cast와 dynamic_cast가 있다.
  • static : 정적인 시간(컴파일 타임)에 발생하는 캐스팅

    • ex) double->int, int->double
  • 다음과 같은 클래스가 있다고 해보자

#include <iostream>

using namespace std;

class Base {
   public:
    int a = 1;
};

class Drv1 : public Base {
   public:
    void f() {
        cout << "Drv::f()" << endl;
        cout << b << endl;
    }
    float b = 3.14;
};


int main() {
        Base *b = new Drv1;
        delete b;
        // Drv *d1 = b는 불가능
}
  • b에서는 멤버 변수 a만 접근이 가능하다

  • b에서도 자식 클래스의 고유 멤버에 접근을 해보고 싶다

    • 그러면 b가 가리키는 객체를 Drv *d1 = b로 넣을 수 있지 않을까?

      • 오류가 발생한다. 자식 클래스의 Base *b = new Drv1;는 자동으로 업캐스팅이 되기 때문에 가능하지만, 다운 캐스팅은 자동으로 이뤄지도록 허용하지 않는다. 명시적으로 처리해야 한다.
int main() {
        Base *b = new Drv1;
        Drv1* d1 = (Drv1*)b;
        d1->f();
        delete b;
}
  • Drv1의 f()가 정상적으로 실행된다

  • 다음과 같은 경우를 생각해보자

int main() {
        Base *b = new Drv1;
        int *a = new int(5)
        Drv1* d1 = (Drv1*)a;
        d1->f();
        delete b;
}
  • b를 형변환해야 하는데, 실수로 a를 형변환해버렸다.

    • 이렇게 해도 컴파일 과정에서 오류는 발생하지 않는다. (어차피 형변환해도 주소는 같으니까)

      • 런타임 에러는 발생할 수 있음
  • 이러한 형변환이 상식적인지 아닌지 검사를 해줬으면 좋겠다.

    • 그래서 등장하는게 static_cast이다.

    • 너무 말이 안되는 형변환을 방지해준다. 전혀 다른 타입의 포인터끼리 형변환을 하는 경우 등을 방지해줌

  • Drv *d1 = static_cast<Drv1*>(a); 를 작성하면 에러가 발생함

  • static_cast는 업캐스팅, 다운캐스팅, int->double, double->int 과 같은 형변환을 지원한다

int main() {
        Base *b = new Drv1;
        int *a = new int(5)
        Drv *d1 = static_cast<Drv1*>(b);
        d1->f();
        delete b;
}
  • static_cast의 본질적인 문제점이 한 가지 더 있다.

  • 자식 클래스 하나를 더 만들어 보자

class Drv2 : public Base {
   public:
    void f() {
        cout << "Drv2::f()" << endl;
        cout << c << endl;
    }
    int c = 3;
};

int main() {
    Base *b = new Drv2;
    Drv2 *d2 = static_cast<Drv2*>(b);
    d2->f();
    delete b;
}
  • 3이 출력된다

  • 만약 실수로 b가 Drv2가 아닌 Drv1을 가리키게 된다면?

int main() {
    Base *b = new Drv1;
    Drv2 *d2 = static_cast<Drv2*>(b);
    d2->f();
    delete b;
}
  • 컴파일러는 b가 어떤 객체의 타입(Base, Drv1, Drv2)을 가리키고 있는지는 신경쓰지 않는다

    • static_cast<Drv2*>(b);에서 컴파일러는static_cast를 Drv1*으로 하든, Drv2*로 하든 신경쓰지 않는다. Drv1과 Drv2가 b 타입(Base)의 자식인지만 신경쓴다
  • 위 코드에서는 실제로 가리키는 객체는 Drv1이지만, Drv2로 형변환을 해주고 있다.

  • 실제로 실행해보면, 실행은 되지만 이상한 값을 출력한다

    • 1078523331가 출력된다
  • Base* b는 주솟값을 담고 있다.(Drv1 객체가 저장된 공간을 가리킨다)

  • Drv *d2 = static_cast<Drv2*>(b);에서

    • d2라는 포인터가 생기고

      • b처럼 똑같은 100번지를 담게 된다

      • 그리고 Drv1 타입이지만 Drv2 타입이라고 착각을 하게 된다

  • Drv1가 저장된 메모리의 첫 4바이트에는 a(1)가 저장되어 있고, 그 다음 4바이트에는 b(3.14)가 저장되어 있다.

  • d2->f()를 호출하게 되면, 어떤 동작이 이루어질까?

    • 우리가 예전에 this 포인터를 배우면서, 멤버 함수는 객체 안의 공간에 저장되는 것은 아니라고 배웠다.

    • 멤버함수를 실행하면 어떤 객체의 멤버함수를 실행해야 하는지를 this 포인터를 통해서 전달된다고 배웠다.

    • 그래서 d2->f()에서 멤버 함수의 this 포인터에 d2에 저장되어있는 100번지가 전달되고, c를 출력하는 것은 this->c를 출력하는 것이다.

    • 만약 d2가 Drv2 인스턴스를 정상적으로 가리켰다면, 첫 4바이트(a) 다음 4바이트에 저장되어있는 c의 값을 출력하게 될 것이다.

    • 하지만 지금 d2가 Drv1 인스턴스를 가리키고 있으므로, 첫 4바이트(a) 다음 4바이트에 저장되어있는 b의 값을 출력하게 될 것이다.

      • 3.14는 정규화를 통해 binary로 저장되어있다.
    • Drv2의 두번째 4바이트에는 int로 저장되어 있으므로, float 포맷으로 저장되어 있는 값을, int로 읽은 값이 출력되는 것이다.

      • 3.14가 float 포맷으로 저장된 binary 값을, int로 해석해서 읽은 1078523331가 출력된다.
  • 그래서 static_cast를 사용할 때 타입을 잘 고려해야 한다

    • 코드가 복잡해져서, 프로그래머가 Base *b에서 b가 가리키는 타입이 무엇인지 알기 어려운 경우가 발생할 수 있다.

    • 객체가 어떤 타입인지 런타임에 알 수 있으면 좋지 않을까? --> Dynamic_cast

      • 다음 게시글에서 다룰 예정

참조) 두들낙서 C/C++ 강좌

'Programming Language(Sub) > C++' 카테고리의 다른 글

[C++] 객체 지향 프로그래밍의 4대 원칙(원리)  (0) 2024.06.28
[C++] 상속에서의 형변환 - RTTI와 dynamic_cast  (0) 2024.06.28
[C++] 상속에서의 형변환 - 업캐스팅(Upcasting)  (0) 2024.06.27
[C++] 생성/소멸자 실행 순서와 가상 소멸자  (0) 2024.06.27
[C++] 순수 가상 함수와 추상 클래스  (0) 2024.06.27
'Programming Language(Sub)/C++' 카테고리의 다른 글
  • [C++] 객체 지향 프로그래밍의 4대 원칙(원리)
  • [C++] 상속에서의 형변환 - RTTI와 dynamic_cast
  • [C++] 상속에서의 형변환 - 업캐스팅(Upcasting)
  • [C++] 생성/소멸자 실행 순서와 가상 소멸자
lumana
lumana
배움을 나누는 공간 https://github.com/bebeis
  • lumana
    Brute force Study
    lumana
  • 전체
    오늘
    어제
    • 분류 전체보기 (463)
      • 개발 일지 (0)
        • Performance (0)
        • TroubleShooting (0)
        • Refactoring (0)
        • Code Style, Convetion (0)
        • Architecture (0)
      • Software Engineering (36)
        • Test (8)
        • 이론 (18)
        • Clean Code (10)
      • Java (72)
        • Basic (5)
        • Core (21)
        • Collection (7)
        • 멀티스레드&동시성 (13)
        • IO, Network (8)
        • Reflection, Annotation (3)
        • Modern Java(8~) (13)
        • JVM (2)
      • Spring (53)
        • Framework (12)
        • MVC (23)
        • Transaction (3)
        • AOP (11)
        • Boot (0)
        • AI (0)
      • DB Access (16)
        • Jdbc (1)
        • JdbcTemplate (0)
        • JPA (14)
        • Spring Data JPA (0)
        • QueryDSL (0)
      • Computer Science (130)
        • Data Structure (27)
        • OS (14)
        • Database (10)
        • Network (21)
        • 컴퓨터구조 (6)
        • 시스템 프로그래밍 (23)
        • Algorithm (29)
      • HTTP (8)
      • Infra (1)
        • Docker (1)
      • 프로그래밍언어론 (15)
      • Programming Language(Sub) (77)
        • Kotlin (1)
        • Python (25)
        • C++ (51)
        • JavaScript (0)
      • FE (11)
        • HTML (1)
        • CSS (9)
        • React (0)
        • Application (1)
      • Unix_Linux (0)
        • Common (0)
      • PS (13)
        • BOJ (7)
        • Tip (3)
        • 프로그래머스 (0)
        • CodeForce (0)
      • Book Review (4)
      • Math (3)
        • Linear Algebra (3)
      • AI (7)
        • DL (0)
        • ML (0)
        • DA (0)
        • Concepts (7)
      • 프리코스 (4)
      • Project Review (6)
      • LegacyPosts (11)
      • 모니터 (0)
      • Diary (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
lumana
[C++] 상속에서의 형변환 - 다운캐스팅
상단으로

티스토리툴바