가상 함수(function)란?
가상 함수(function)는 자식 클래스(class)에서 재정의(override)할 수 있는 함수(function)입니다. 부모 클래스(class)에서 virtual 키워드를 사용하여 선언하고, 자식 클래스(class)에서 override 키워드로 재정의합니다.
기본 개념
virtual 키워드
- 부모 클래스(class)에서 함수(function)를 가상 함수(function)로 선언
- 자식 클래스(class)에서 재정의 가능하도록 허용
override 키워드
- 자식 클래스(class)에서 부모의 가상 함수(function)를 재정의
- 부모 클래스(class)의 함수(function)를 완전히 대체
기본 예시: 동물 클래스(class)
부모 클래스 (Animal)
public class s01_animal : MonoBehaviour
{
public string animalName;
// 가상 함수
public virtual void Speak()
{
Debug.Log("동물이 말하는 소리를 냅니다.");
}
}
자식 클래스 (Cat)
public class s02_cat : s01_animal
{
// 가상 함수 재정의
public override void Speak()
{
Debug.Log("고양이는 야옹~ 야옹~");
}
}
자식 클래스 (Dog)
public class s03_dog : s01_animal
{
// 가상 함수 재정의
public override void Speak()
{
Debug.Log("강아지는 멍멍~ 멍멍~");
}
}
사용 예시
public class s04 : MonoBehaviour
{
void Start()
{
s01_animal myDog = new s03_dog();
s01_animal myCat = new s02_cat();
myDog.Speak(); // "강아지는 멍멍~ 멍멍~"
myCat.Speak(); // "고양이는 야옹~ 야옹~"
}
}
핵심: 부모 클래스(class) 타입(type)으로 선언했지만, 실제 객체(object)의 타입(type)에 따라 재정의된 함수(function)가 호출됩니다!
실전 예시 1: 적(Enemy) 클래스(class)
부모 클래스(class)
public class s05_enermy : MonoBehaviour
{
public virtual void Die()
{
Debug.Log("적이 죽었습니다.");
}
}
자식 클래스(class)들
// 고블린
public class s05_enermy_child01 : s05_enermy
{
public override void Die()
{
Debug.Log("고블린이 죽었습니다.");
}
}
// 슬라임
public class s05_enermy_child02 : s05_enermy
{
public override void Die()
{
Debug.Log("슬라임이 녹아내렸습니다.");
}
}
// 오크
public class s05_enermy_child03 : s05_enermy
{
public override void Die()
{
Debug.Log("오크가 쓰러졌습니다.");
}
}
사용 예시
public class s05_enermy_final : MonoBehaviour
{
void Start()
{
s05_enermy enermyGoblin = new s05_enermy_child01();
s05_enermy enermySlime = new s05_enermy_child02();
s05_enermy enermyOrc = new s05_enermy_child03();
enermyGoblin.Die(); // "고블린이 죽었습니다."
enermySlime.Die(); // "슬라임이 녹아내렸습니다."
enermyOrc.Die(); // "오크가 쓰러졌습니다."
}
}
실전 예시 2: 무기(Weapon) 클래스(class)
부모 클래스(class)
public class s06_weapon : MonoBehaviour
{
// 가상 함수
public virtual void Attack()
{
Debug.Log("Weapon Attacks");
}
}
자식 클래스(class)들
// 레이저
public class s06_weapon_child01 : s06_weapon
{
public override void Attack()
{
Debug.Log("Laser attacks");
}
}
// UZI
public class s06_weapon_child02 : s06_weapon
{
public override void Attack()
{
Debug.Log("UZI attacks");
}
}
사용 예시
public class s06_weapon_final : MonoBehaviour
{
void Start()
{
s06_weapon weaponLaser = new s06_weapon_child01();
s06_weapon weaponUZI = new s06_weapon_child02();
weaponLaser.Attack(); // "Laser attacks"
weaponUZI.Attack(); // "UZI attacks"
}
}
실전 예시 3: 캐릭터 타입(type)
부모 클래스(class)
public class s07_girl : MonoBehaviour
{
// 가상 함수
public virtual void type()
{
Debug.Log("일반적인 캐릭터");
}
}
자식 클래스(class)들
// 귀여운 타입
public class s07_girl_child01 : s07_girl
{
public override void type()
{
Debug.Log("귀여운 캐릭터");
}
}
// 쿨한 타입
public class s07_girl_child02 : s07_girl
{
public override void type()
{
Debug.Log("쿨한 캐릭터");
}
}
virtual vs override 비교
| 키워드 | 사용 위치 | 역할 |
|---|---|---|
virtual |
부모 클래스(class) | 자식 클래스(class)에서 재정의 가능하도록 허용 |
override |
자식 클래스(class) | 부모의 가상 함수(function)를 재정의 |
가상 함수(function)의 장점
- 다형성(Polymorphism): 같은 타입(type)으로 선언해도 실제 객체(object)에 따라 다른 동작
- 유연성: 각 자식 클래스(class)마다 다른 구현 가능
- 코드 재사용: 공통 인터페이스(interface) 제공하면서 개별 구현 허용
주의사항
- virtual과 override는 쌍으로 사용: 부모에서
virtual, 자식에서override - override 없이는 재정의 불가:
virtual함수(function)를 재정의하려면 반드시override사용 - private 함수(function)는 virtual 불가:
virtual은public또는protected와 함께 사용
abstract vs virtual
| 키워드 | 특징 | 사용 시기 |
|---|---|---|
virtual |
기본 구현 제공, 선택적 재정의 | 기본 동작이 있는 경우 |
abstract |
구현 없음, 반드시 재정의 필요 | 기본 동작이 없고 반드시 구현해야 하는 경우 |
실전 활용 팁
팁 1: 공통 인터페이스(interface) 제공
// 모든 무기가 Attack() 함수를 가지지만, 각각 다른 방식으로 공격
public class Weapon
{
public virtual void Attack() { }
}
팁 2: 기본 동작 제공
// 기본 동작을 제공하되, 필요시 재정의 가능
public class Enemy
{
public virtual void Die()
{
Debug.Log("적이 죽었습니다."); // 기본 동작
}
}
팁 3: 다형성(polymorphism) 활용
// 같은 타입으로 선언해도 실제 객체에 따라 다른 동작
Enemy[] enemies = { new Goblin(), new Slime(), new Orc() };
foreach (Enemy enemy in enemies)
{
enemy.Die(); // 각각 다른 메시지 출력
}