본문 바로가기
Programming/c++ Design Pattern

[c++] Upcasting

by 드가보자 2023. 11. 28.

 

class Animal
{
public:
	int age;
};
class Dog : public Animal
{
public:
	int color;
};
int main()
{
	Dog d;
	Dog* p1 = &d; 
	Animal* p3 = &d;
	p3->age = 10;
	static_cast<Dog*>(p3)->color = 10; 
}

 

"Animal"  클래스는 age라는 public 멤버 변수를 가지고 있습니다.

 

"Dog" 클래스는 Animal 클래스를 public으로 상속받고 있습니다. 따라서 Dog 클래스는 Animal 클래스의 age 멤버 변수를 상속받고, 추가로 color라는 public 멤버 변수를 가지고 있습니다.

 

Dog 객체 d를 만들고, d의 pointer p1에 d(Dog 객체)의 주소 값을 입력해준다.

Animal pointer p3에 마찬가지로 동일 주소를 입력해준다. 

 

여기서 만약에 int* p2 = &d 이렇게 line을 적었다면, Error가 발생한다. -> c++은 Type에 정말 예민하기 때문에, 
Pointer가 가르키는 부분의 type이 중요하다.

 

그리고 사실 Dog의 주소 값인데 왜 
Animal* p3 = &d; 이게 되는거지? 라고 의문이 들 수 있는데

 

기반 class 타입의 pointer에 파생 class 객체의 주소를 담는 것은 아무 문제가 되지 않는다. 

이를 Upcasting이라고 부르고, 정말 많이 사용한다.

Animal* p3 = &d; => 해당 부분이 Upcasting이라고 생각하면 된다.

Dog 객체 d를 생성했을 때, 메모리 구조는 다음과 같다. 부모 클래스가 더 상위 메모리를 차지하고 , 그 다음 파생 클래스가 위치하게 된다.

 

그리고 기반 class pointer로는 파생 class의 member 변수에게 접근은 불가능하고, 기반 class 멤버 변수에는 접근 못한다.

Compiler 입장에서는 엥?? 쟤 Animal인데 int color가 어딨어? 이런 느낌이 되는 것이다.

c++는 static type check 언어이다, 즉 Compile 시간에 얘의 type이 뭔지 체크한다.

즉 Compiler가 p3 ?? 오케이 너 애니멀 확인 ! 이런 느낌이다.  -> 안전성이 뛰어난 언어의 특징이다.

 

반면에 python은 "Dynamic type check" 언어인데, 실행 시간에 type을 체크해서 속도가 느리지만 사용하긴 되게 쉬운 편이다.

 

그래서 결론적으로 p3->color 이런 접근은 불가능하다. p3->age=10은 가능하겠지만, 이게 가능한 이유는 age 변수가 public이라서 그렇다.

 

p3가 Dog class 멤버 변수인 color에 접근하는 것이 아예 불가능 한 것은 아닌데, 방법은 Casting이다.

Casting에는 크게 두 가지가 있고, Static Casting과 Dynamic Casting이다.

  1. Static Cast
    Compile 시간에 명시적으로 형 변환을 한다.
    사용자가 p3가 사실 어떤 객체인지 확신이 있어야한다.
    "얘 Animal Pointer이기는 한데, 사실은 Dog야!" 이거를 Compiler한테 알려주는 것으로 
    Compiler가 compile 시간에 아 얘 Dog 객체구나??? 이렇게 알게 해주는거다
    영구적으로 p3가 Dog pointer로 바뀌는거는 아니다 . => 임시적으로 해당 line에서 바뀌는거다. 

  2. Dynamic Cast
    Runtime에 객체의 type을 파악하는 casting 방식이다. 보통 좋은 Design으로 간주하지 않고, 시간이 오래걸린다.
    확인하는 방법은 Runtime에 pointer가 가르키는 곳에서, 가상 함수 테이블(Virtual Table) 을 확인해서 객체의 실제 Type을 확인한다.
    그래서 해당 객체 class에 가상 함수가 없다면, 가상 함수 테이블은 채워지지 않고 Runtime에 객체 Type을 확인 할 방법은 없다. 
// upcasting3.cpp
class Animal 
{
public: 
	int age;
};
class Cat : public Animal 
{
};
class Dog : public Animal
{
public:
	int color;
};

//void NewYear(Dog* pDog) //Dog 만 가능
void NewYear(Animal* p)
{
	++(p->age); 
	
}

int main()
{
	Animal a; NewYear(&a);
	Dog    d; NewYear(&d);
}

 

동물들의 나이를 모두 +1 시키는 예제이다. 
만약 NewYear 함수에 (Dog* p) 로 함수 변수를 선택한다면, 문제가 된다.

만약에 이렇게 한다면 compiler는 해당 pointer를 따라가면, age와 color변수는 무조건 있어야 된다고 생각하는데

Animal a; NewYear(&a) 라인에서 주소를 따라가보면 color 변수가 없는 것이다 !.. 그래서 Compile Error가 발생한다.

 

 

 

 

 

'Programming > c++ Design Pattern' 카테고리의 다른 글

[c++ Design Pattern] Template Method  (0) 2023.11.29
[C++] OCP Code 예제  (1) 2023.11.29
[C++] 추상 클래스  (0) 2023.11.29
[C++] 가상 함수  (1) 2023.11.28
c++ 생성자  (0) 2023.11.28