클래스 (객체지향)

업 캐스팅(Upcasting)과 다운캐스팅(Downcasting)

ShadowEye 2021. 6. 27. 00:06

1. Upcasting과 Downcasting이란 무엇일까?

 


Upcasting은 상속받은 자식 클래스를 부모클래스로 형변화 하는것을 말한다

Downcasting은 기본 클래스 포인터로 처리 할 수 ​​있습니다. 이것은 업 캐스팅입니다.

간단하게 설명하면 이렇지만 단순히 다른 데이터 유형으로 변환한다고 생각하면 큰문제가 발생할수있다.

 

2. Upcasting 

 

보시다시피 Manager(매니저)와 서기(Clerk)는 모두 직원(Employee)이고, 둘 다 사람(Person)이다.


이는 Manager 및 Clerk 클래스가 Person 클래스의 속성을 상속하는 Employee 클래스의 속성을 상속 함을 의미한다.

예를 들어, Manager 와 Clerk  모두 이름과 성으로 식별되고 급여가 있음을 지정할 필요가 없다. 

그들에 대한 정보를 보여주고 그들의 급여에 보너스를 추가 할 수 있다. 

 

Employee 클래스에서 이러한 속성을 한 번만 지정하면 된다.

다음 코드를 보자

#include <iostream>
using namespace std;
class Person
{
  //content of Person
};
class Employee:public Person
{
public:
  Employee(string fName, string lName, double sal)
  {
    FirstName = fName;
    LastName = lName;
    salary = sal;
  }
  string FirstName;
  string LastName;
  double salary;
  void show()
  {
    cout << "성: " << FirstName << " 이름: " << LastName << " 월급: " << salary<< endl;
  }
  void addBonus(double bonus)
  {
    salary += bonus;
  }
};
class Manager :public Employee
{
public:
  Manager(string fName, string lName, double sal, double comm) :Employee(fName, lName, sal)
  {
    Commision = comm;
  }
  double Commision;
  double getComm()
  {
    return Commision;
  }
};
class Clerk :public Employee
{
public:
  Clerk(string fName, string lName, double sal, Manager* man) :Employee(fName, lName, sal)
  {
    manager = man;
  }
  Manager* manager;
  Manager* getManager()
  {
    return manager;
  }
};
void congratulate(Employee* emp)
{
  cout << "생일 축하!!!" << endl;
  emp->addBonus(200);
  emp->show();
};
int main()
{
    //employee의 포인터를 가르킨다
    Employee* emp;
    
    //매니저와 서기를 선언한다.
    Manager m1("유", "재석", 3000, 0.2);
    Clerk c1("정","준하", 1000, &m1);
    
    //manager를 employee로 업캐스팅
    emp = &m1;
    
    //empolyee에 있는 함수 사용 가능
    cout<<emp->FirstName<<endl;
    cout<<emp->salary<<endl;
    
    //데이터 유형을 employee로 변환 시켰기 때문에 manager 함수 사용 불가
    //cout<<emp->getComm();
    
    congratulate(&c1);
    congratulate(&m1);
    
    cout<<"매니저 "<<c1.FirstName<<" 는 "<<c1.getManager()->FirstName;
}


코드를 돌려보면 형변환이후 기존의 자식클래스에 있는 함수및 변수를 사용 할수없다.

왜 그런걸까?? 

 

 

다음을 그림은 업캐스팅시 메모리 읽는 공간을 보여준다.

 

포인터가 가르키는 위치에서 employee크기만큼 자료를 읽는다.

그러니 그외에부분인 Manager에 종속된 함수를 사용할수없는것이다.

 

3. downcasting

 

다운캐스팅은 위에서도 설명했다시피 업캐스팅과 반대되는 의미이다

 

업캐스팅이 자식클래스를 부모클래스로 데이터유형을 변경한다면

 

다운캐스팅은 부모클래스를 자식클래스로 데이터유형을 변환하는 프로세스이다.

 

다만 다운캐스팅은 업캐스팅에 비해 안전하지않다.

 

그이유는 위에예제에 manager는 항상 person이지만 person은 manager가 될수도 있고 clerk가 될수도 있기때문이다.

다운 캐스팅에서는 명시적 캐스트를 사용해야한다.

//pointer to base class object

Employee* emp;

//object of derived class

Manager m1("유", "재석", 3000, 0.2);

//implicit upcasting

emp = &m1;

//explicit downcasting from Employee to Manager

Manager* m2 = (Manager*)(emp);

자 위와같은경우 무엇이 문제일까??

위에그림을 다시한번 보자

 

아까는 매니저 부분중에 Employee로 형변환하면서 일부분만을 사용하지만 이번에는 Employee부분보다 훨씬 큰 메모리부분을 Manager는 요구한다 즉 Employee에 존재하지않지만 Manager에는 존재하는 Comision에 대한 정보가없는 메모리에 액세스할수있다는 위험이존재한다


Comision이 쓰레기값일경우 재지정하면 상관없지만 다른곳에서 할당하고있는경우(Array같은걸쓸경우 객체가 사용중일경우) 그값을 변경시키거나 값이 이상해지는 오류가날수있다.

그렇기때문에 이를해결하기위한 방법으로 c++에서는 동적캐스트(Dynamic cast)를 제공한다.

 

4. Dynamic Cast

dynamic_cast는 한 유형을 다른 유형으로 안전하게 변환하는 연산자이다. 캐스팅이 가능하고 안전한 경우 변환 된 객체의 주소를 반환하고 그렇지 않으면 nullptr을 반환한다.

 

Employee e1("Peter", "Green", 1400);
Manager* m3 = dynamic_cast<Manager*>(&e1);
if (m3)
  cout << m3->getComm() << endl;
else
  cout << "Can't  cast from Employee to Manager" << endl;

이런식으로 간단하게 쓸수있는 매우 편리한기능이다.