Upcasting
• 기반 클래스 포인터로 파생 클래스 객체를 가리킬 수 있다.
• 기반 클래스 포인터로는 기반 클래스 멤버만 접근 가능
• 파생 클래스 접근하려면 static_cast 필요
[예제] upcasting 이란
class Animal
{
public:
int age;
};
class Cat : public Animal
{
public:
int leg;
};
int main()
{
Cat c;
Cat* p1 = &c; // ok
Animal* p2 = &c; // ok (upcasting)
p2->age = 5; // ok
}
Animal* p2 는 기반 클래스 age 변수에만 접근 가능하다.
그렇기에 파생 클래스의 leg 변수에 접근하면 error
[예제] upcasting 주의사항
class Animal
{
public:
int age;
};
class Cat : public Animal
{
public:
int leg;
};
int main()
{
Cat c;
Animal* p = &c;
// error
// p->leg = 10;
}
[예제] static_cast 활용
class Animal
{
public:
int age;
};
class Cat : public Animal
{
public:
int leg;
};
int main()
{
Cat c;
Animal* p = &c;
static_cast<Cat*>(p)->leg = 10; // ok
}
upcasting 활용
기반 클래스(Animal)로 부터 파생된
모든 객체를 컨테이너에 보관할 수 있다.
#include <vector>
class Animal {};
class Dog : public Animal {};
class Cat : public Animal {};
int main()
{
std::vector<Dog*> v1; // Dog만 보관 가능
std::vector<Animal*> v2; // 모든 동물을 보관 가능
}
상속이 기존 타입의 확장으로도 볼 수 있지만
A와 B를 묶어서 관리하고 싶은 경우에도 활용할 수 있다.
[예제]
하나의 기반 클래스로부터 파생된 클래스를 위한 공통함수
class Animal
{
public:
int age;
};
class Cat : public Animal {};
class Dog : public Animal {};
void PlusAge(Dog* p) // Dog만 받을 수 있다.
{
++(p->age);
}
int main()
{
Dog d; PlusAge(&d); // ok
Cat c; PlusAge(&c); // error
Animal a; PlusAge(&a); // error
}
void PlusAge(Dog* p) 에서는 Dog 객체만 받을 수 있지만
void PlusAge(Animal* p) 에서는 기반+파생 클래스 객체를 받을 수 있다.
[예제] upcasting 적용
class Animal
{
public:
int age;
};
class Cat : public Animal {};
class Dog : public Animal {};
void PlusAge(Animal* p) // 모든 동물을 받을 수 있다.
{
++(p->age);
}
int main()
{
Dog d; PlusAge(&d); // ok
Cat c; PlusAge(&c); // ok
Animal a; PlusAge(&a); // ok
}
static_cast 이용한 down cast
class Animal
{
public:
int age;
};
class Cat : public Animal {};
class Dog : public Animal
{
public:
int leg;
};
void PlusAge(Animal* p)
{
++(p->age);
Dog* pDog = static_cast<Dog*>(p);
pDog->leg = 4; // ??? -> error
}
int main()
{
Cat c; PlusAge(&c);
}
Cat 객체를 보내주고 Dog로 static_cast 한다면
컴파일은 성공하지만 실행하면 Error 발생!
이는 static_cast가 실제 Dog 여부 상관없이 casting 처리하기 때문이다.
dynamic_cast로 실제 객체인지 확인할 수 있다.
단, 반드시 가상함수가 한개 이상 있는 경우만 사용 가능
(가상함수 테이블 안에 있는 타입 정보를 사용해서 객체 타입을 조사하기 때문)
class Animal
{
public:
virtual ~Animal() {}
int age;
};
class Cat : public Animal {};
class Dog : public Animal
{
public:
int leg;
};
void PlusAge(Animal* p)
{
++(p->age);
// 실체 객체가 Dog가 아니라면 0 반환
Dog* pDog = dynamic_cast<Dog*>(p);
if (pDog != nullptr)
{
pDog->leg = 4; // error
}
}
int main()
{
Cat c; PlusAge(&c);
}
dynamic_cast
→ 실행 시 조사하기 때문에 overhead가 크다. (속도가 상대적으로 느리다.)
static_cast
→ 컴파일 시간에 조사하기 때문에 overhead가 적다
실제 객체를 보장할 수 있다면 static_cast 사용하는 것이 좋지만
그렇지 않다면 안정성을 위해서는 dynamic_cast 사용하는 것이 좋다.
📌 [C++] static_cast 사용 방법 및 필요성
[C++] static_cast 사용 방법 및 필요성
static_cast 필요성C 언어에서도 type casing 가능하지만논리적으로 위험한 캐스팅을 대부분 허용해서개발자 의도 인지 실수 인지 구분하기 어렵다. [예제 코드]void* -> 다른 타입으로 캐스팅하는 것
zoosso.tistory.com
댓글