ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity]유니티 입문하기 6-9(상속)
    프로그래밍 2023. 8. 14. 20:38

    이전 글에서는 객체 지향의 두 번째 특성인 캡슐화에 대해서 알아보았습니다.

    이번에는 객체 지향 언어의 세 번째 특성, 상속에 대해서 알아보겠습니다.


    여기, 앞서 배웠던 요소들을 전부 이용해 만든 Human 클래스가 있습니다.

    class Human
    {
        private string name;
        private int age;
        private float height;
        
        public Human(string _name, int _age, float _height)
        {
        	name = _name;
            age = _age;
            height = _height;
        }
        
        public string Name
        {
        	get { return name; }
            set { name = value; }
        }
        public int Age
        {
        	get { return age; }
            set { age = value; }
        }
        public float Height
        {
        	get { return height; }
            set { height = value; }
        }
        
        private void Eat(){
        	Debug.Log("eat");
        }
        private void Walk(){
        	Debug.Log("walk");
        }
        private void Sleep(){
        	Debug.Log("sleep");
        }
    }

    여기서 우리가 Adult과 Baby라는 클래스를 구분해서 만들어야 하게 되었다고 생각해 봅시다.

    어른과 아이는 전부 사람이므로 서로 간에 거의 동일한 메서드와 필드를 가지게 될 것을 예측하는 것은 어렵지 않겠죠.

    이를 상속 없이 구현하고자 하면, 같은 코드가 반복되게 될 것입니다.

    앞서 계속해서 말했듯, 반복되는 코드는 프로그램의 용량을 잡아먹을 뿐만 아니라 필드나 메서드를 수정해야 할 때 그 각각을 전부 수정해야 한다는 문제점이 있습니다.

    그래서 이를 해결하기 위해 나온 것이 바로 상속입니다.

     

    상속

    상속이란 뜻 그대로 어떠한 클래스를 물려받는 것을 의미합니다.

    어떠한 클래스를 상위 클래스와 하위 클래스로 나눈 뒤, 상위 클래스의 코드를 그 클래스의 하위 클래스가 물려받아 사용하게 되는 것이죠.

    상위 클래스는 부모 클래스(parent class), 베이스 클래스(base class) 등으로도 불리며, 

    하위 클래스는 자식 클래스(child class), 파생 클래스(derived class) 등으로도 불립니다.

     

    상속을 간단히 표현한 도식

    위 그림과 같이 여러 개의 클래스가 하나의 클래스에게 상속받는 것은 가능하지만, 반대로 하나의 클래스에 여러개의 클래스를 상속받는 다중 상속은 불가능합니다. 실제로 친부모와 친자식간의 관계를 생각해 보면 쉽게 이해할 수 있을 겁니다.

     

    그럼 기존에 만들어 두었던 Human 클래스를 상속받는 Adult와 Baby 클래스를 만들어보도록 하죠.

    class Adult : Human
    {
        public Adult(string _name, int _age, float _height) : base(_name, _age, _height){}
    }
    
    class Baby : Human
    {
        public Baby(string _name, int _age, float _height) : base(_name, _age, _height){}
    }

    상속은 위와 같이 하위 클래스의 이름 뒤에 콜론(:)을 붙여서 이루어지게 됩니다.

    이제 Adult 와 Baby 클래스는 상속을 통해 Human 클래스가 가지는 필드와 메서드를 그대로 가지게 됐으며, 그 덕분에 똑같은 코드를 여러 번 사용할 필요가 없어졌습니다.

    이렇게 적어두는 것만으로도, Adult와 Baby는 name, age와 같은 필드와 Eat, Sleep 같은 메서드를 똑같이 가지게 되는 것이지요.

    다만 생성자는 조금 다른 모습을 하고 있는데, 여기 등장하는 base라는 키워드는 나중에 다시 알아보도록 합니다.

     

    위와 같이 클래스를 정의한 뒤 아래와 같이 클래스를 테스트해보면, 무사히 출력된 것을 볼 수 있습니다.

    void Start() {
            Adult John = new Adult("john", 28, 173);
            Baby Lisa = new Baby("lisa", 3, 95);
            Debug.Log(John.Name);
            Debug.Log(Lisa.Age);
    }

    다만 자식 클래스라고 하여도 부모 클래스의 모든 것을 상속받는 것이 아니며, 우리는 접근 제한자를 통해 이 범위를 조절할 수 있습니다.

    앞서 우리가 public, internal, protected, private의 네 가지의 접근 제한자에 대해 배울 때 pretected에서 언급된 파생 클래스가 뭔지 이제는 알고 있죠.

    그러니 pretected 필드인 address를 하나 만들어봅시다.

    class Human
    {
        public string name;
        public int age;
        public float height;
        protected string address;
        //(이하생략)
    }
    
    class Adult : Human
    {
        public Adult(string _name, int _age, float _height) : base(_name, _age, _height){}
        
        public void PrintAddress()
        {
            Debug.Log(address);
        }
    }
    
    class Baby : Human
    {
        public Baby(string _name, int _age, float _height) : base(_name, _age, _height){}
    
        public void PrintAddress()
        {
            Debug.Log(address);
        }
    }

    이렇게 하면 외부에서는 address에 접근할 수 없지만, Human의 하위 클래스인 Adult와 Baby에서는 아무 문제 없이 address에 접근할 수 있음을 알 수 있습니다.

     

    this와 base

    이제 상속 관계에서 매우 중요한 역할을 하는 this와 base에 대해 알아봅시다.

    this

    this는 말 그대로 현재 객체를 가리키는 키워드입니다.

    여태껏 생성된 객체에 접근을 할 때, 어디에서 접근하느냐에 따라 다음과 같은 방식을 취했는데요,

    //클래스 외부
    Human tom = new Human();
    tom.Age = 10;
    
    //클래스 내부
    int age;
    age = 10;

    이 중 클래스 내부에서 접근할 때에는 별 다른 키워드가 없어도 직관적으로 이해가 가능했지만, 사실 이는 this 키워드가 생략된 것입니다.

    멤버에 접근하기 위해서는 객체를 통할 필요가 있고, 그것은 해당 멤버가 정의된 클래스 내부에서도 예외가 아니기 때문이죠.

    this.age = 10;

    따라서, 어떠한 멤버가 정의된 클래스 내부에서 해당 멤버에 접근할 때에는, 위와 같이 this 키워드를 이용해 멤버에 접근하게 됩니다.

     

    예를 들어, 여기에 Human 클래스의 생성자가 있습니다.

    public Human(string _name, int _age, float _height)
    {
        name = _name;
        age = _age;
        height = _height;
    }

     

    여태까지는 멤버 변수인 age와 Human 생성자의 인수인 age를 구분할 수 있도록 인수 쪽 age 앞에 밑줄을 붙였지만, this 키워드를 활용하면 멤버 변수인 age와 인수인 age를 아래와 같이 this. 키워드를 통해 구분할 수 있게 됩니다.

    public Human(string name, int age, float height)
    {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    또한 this가 현재 객체를 가리키는 키워드이기 때문에, 만약 this()와 같이 작성하게 된다면 이는 해당 객체의 생성자를 의미하게 됩니다.

    즉, 생성자를 여러 개 만들어야 하는 등의 이유로 클래스 내부에서 자신의 생성자를 호출할 때, this()를 이용해 중복 코드를 줄일 수 있습니다.

     

    base

    base도 this와 비슷한 역할을 하는데요, base는 현재 객체의 부모 클래스를 가리키는 키워드입니다.

    따라서 this와 똑같이 base.(멤버명)을 활용해, 부모 클래스의 멤버에 접근할 수 있게 됩니다.

    바로 예제 코드와 결과를 보죠.

    void Start()
    {
        Adult maria = new Adult("maria", 10, 12);
        maria.Age = 1;
        maria.SetBaseAge(2);
        Debug.Log(maria.Age);
        Debug.Log(maria.GetBaseAge());
    }
    
    class Adult : Human
    {
        public Adult(string _name, int _age, float _height) : base(_name, _age, _height){}
        
        public int Age;
        public void SetBaseAge(int age)
        {
            base.age = age;
        }
        public int GetBaseAge()
        {
            return base.age;
        }
    }

     

    이제 base 키워드에 대해서 이해했으니, 아까 자식 클래스의 생성자가 다른 형태를 띠었던 것도 이해가 될 겁니다.

    public Adult(string _name, int _age, float _height) : base(_name, _age, _height){}

    Adult 클래스의 생성자를 다시 가져왔는데요, 여기서 클래스의 상속과 비슷하게 콜론(:)이 오고, base 키워드를 사용한 것을 볼 수 있습니다.

    여기서 base()는 this()와 똑같은 사용법으로, 부모 클래스의 생성자를 호출하는 것입니다.

    즉, 이 생성자는 생성자의 매개변수를 그대로 부모 클래스의 생성자의 인수로 전달해 부모 클래스의 생성자를 호출한 것이 됩니다.

Designed by Tistory.