[HCI] 5주차 상속성

정수현·2025년 4월 11일

C#

목록 보기
6/10

상속(Inheritance)

  • 상위 클래스의 기능과 속성을 하위 클래스에게 그대로 물려주는 것

💡 용어 정리

상위 클래스

  • 부모/기반 클래스 (base class)
  • 한 개 이상의 자식 클래스와 관계를 맺는 계층 구조로 표현 가능
  • 공통 부분을 부모 클래스에 구현

하위 클래스

  • 자식/파생 클래스 (derived class)
  • 상속을 받으면 상위 클래스의 모든 멤버필드와 메서드를 사용할 수 있음
  • 서로 다른 부분은 자식 클래스에 구현 가능

클래스의 계층 구조 생성

  • 업캐스팅 : 상속 받은 인터페이스 사용 보장

재사용성 및 확장성

  • 이미 존재하는 클래스를 구체화하여 새로운 클래스 생성
  • 동일한 특성을 재정의할 필요가 없어 서브 클래스가 간결해짐



📍 상속(Inheritance)

  • base 연산자로 상위 객체에 접근한다.
  • 클래스를 정의할 때 : (콜론)을 사용하여 상속 관계를 표시한다.
class Person
{
	...
}
class Student: Person 
{
	...
}
class StudentWorker: Studnet 
{
	...
}
  • Student 클래스 : Person을 상속 받는다.
  • StudentWorker 클래스 : Student를 상속 받는다.

상속 문법

① 상위 클래스에 공통 멤버를 정의한다.

class Person
{
	string name;
    int age;
    public int GetAge() { return age; }
}
  • 상위 클래스 Person에 공통 부분을 정의하였다.

② 하위 클래스에 필요한 멤버를 추가한다.

class Student: Person
{
	int id;
}

class Researcher: Person
{
	string research;
}
  • 하위 클래스 StudentResearcher에 필요한 멤버를 추가하였다.

상속 시 메모리 할당

  • 하위 객체 생성시 상위 객체도 생성되어 데이터의 메모리를 할당한다.

부모 클래스

class Person
{
	string name;
    int age;
}

자식 클래스

class Student: Person
{
	int id;
    ...
}

메모리 생성

  • Student s = new Student();
  • sname | age | id

🍀 상속 예제

public class Point 
{
	private int x, y;
    public void Set(int x, int y)
    {
    	this.x = x;
        this.y = y;
    }
    public void ShowPoint()
    {
    	Console.WriteLine("x + y");
    }
}

public class ColorPoint: Point
{
	private string color;
    public void ShowColorPoint()
    {
    	Console.WriteLine(color);
        ShowPoint();
    }
}
  • Point를 상속받은 ColorPoint 선언
  • ShowColorPoint() : 컬러 점의 좌표 출력
  • ShowPoint() : Point 클래스의 ShowPoint() 호출
public class ColorPointApp
{
	static void Main(string[] args)
    {
    	ColorPoint cp = new ColorPoint();
        cp.Set(3,4);
        cp.SetColor("Red");
        cp.ShowColorPoint();    
    }
}
  • cp.Set(3,4) : Point 클래스의 Set() 메서드 호출
  • cp.SetColor("Red") : 색 지정
  • cp.ShowColorPoint() : 컬러 점의 좌표 출력

📍 부모 클래스 멤버 접근

  • 부모 클래스의 protectedpublic으로 지정된 멤버만을 상속
  • 상속 받은 멤버는 부모 클래스에서 접근 지정자를 유지

Person 클래스 (부모)

class Person
{
	int age;
    protecte String name;
    public int height;
    private int weight;
    
    public int GetAge()
    {
    	return age;
    }    
    
    public void SetAge(int age)
    {
    	this.age = age;
    }	
    
    public int GetWeight()
    {
    	return weight;
    }
    
    public void SetWeight(int weight)
    {
    	this.weight = weight;
    }
}

Student 클래스 (자식)

  • Person 클래스의 private 필드인 age, weightStudent 클래스에서 접근이 불가능하여 부모 클래스인Personpublic 메소드를 통해서만 조작 가능하다.
public class Student: Person
{
	int id;
    
    public int GetID()
    {
    	return id;
    }
    
    void Set()
    {
    	// age = 30; -> 사용 불가
        // weight = 7-; -> 사용 불가
    	name = "Park";
        height =175;        
        id = 1000;
    }
    
    static void Main(string[] args)
    {
    	Student s = new Student();
        s.Set();
        int age = s.GetAge();
        int id = s.GetId();
        
        Console.WriteLine("학생 이름: {0}", s.name);
    }
}
  • s.GetAge() : 상속된 객체에서 부모 객체의 public 메서드 호출
  • s.GetId() : 상속된 객체 자신의 메서드 호출

📍 상위 클래스의 생성자 호출

  • 자식 클래스에는 부모 클래스의 생성자가 상속되지 않으므로 직접 호출해야 함
public class Car 
{
	public Car() { }
    public Car(int wheel) { this.wheel = wheel; }
    ...
}

public class Sedan: Car
{
	Sedan() { }
    Sedan(int wheel) : base(wheel) { }
}
  • 만약 위의 문법과 같이 명시적으로 호출하지 않으면 임시적으로 기반 클래스의 기본 생성자를 호출
  • Sedan() : base()와 동일 -> 암시적 부모 클래스 생성자 호출
  • Sedan(int wheel) : base(wheel) -> 명시적 부모 클래스 생성자 호출

📍 base 키워드

  • 부모 클래스의 멤버를 나타냄
  • 부모 클래스의 멤버를 자식 클래스에서 재정의하였을 경우, 디폴트는 override된 멤버
  • 부모 클래스의 멤버를 사용할 경우 명시
public class Car
{	
	protected bool gasoline;
    protected Car() { gasoline = true; }
    protected Car(int wheel) 
    { 
    	this.wheel = wheel;
        gasoline = false;
    }
}

public class Sedan: Car
{
	private bool gasoline;
    Sedan() 
    { 
    	gasoline = false;
        // base.gasoline = false;
        // this.gasoline - false;
    }
    Sedan(int wheel) : base(wheel) { gasoline = true; }
    public void SedanMove() 
    {
    	if (base.gasoline) ...
        if (this.gasoline) ...
    }
}

📍 base와 this

base

  • 상속 받은 부모 클래스의 생성자 호출
using System;
class A
{
	public A() { Console.WriteLine("A"); }
}
class B: A
{
	public B() { Console.WriteLine("B"); }
    public B(int foo): this()
    {
    	Console.WriteLine("B({0})", foo);
    }
}
class DefaultInitializerTest
{
	public static void Main()
    {
    	A a1= new A();
        B b1 = new B();
        B b2 = new B(100);
    }
}

this

  • 자기 자신에게서 정의한 다른 생성자 호출

🍀 base-this 예제

Point & ColorPoint 클래스

public class Point
{
	private int x, y;
    
    public Point(): this(0, 0) { }
    
    public Point(int x, int y)
    {
    	this.x = x;
        this.y = y;
    }
    
    public void Set(int x, int y)
    {
    	this.x = x;
        this.y = y;
    }
    
    public void ShowPoint()
    {
    	Console.WriteLine("(" + x + ", " + y +")");
    }
}

Point & ColorPoint 클래스 생성자

public class ColorPoint: Point
{
	private string color;
    
    public ColorPoint()
    {
    	this.color = "Green";
    }
    
    public void SetCoor(string color)
    {
    	this.color = color;
    }
    
    public void ShowColorPoint()
    {
    	Console.WriteLine(color);
        ShowPoint();
    }
}

public class ColorPointApp
{
	static void Main() string[] args)
    {
    	Point p = new Point();
        p.ShowPoint();
        ColorPoint cp = new ColorPOint(5, 7, "Blue");
        cp.ShowColorPoint();
        cp = new ColorPoint();
        cp.ShowColorPoint();
    }
    
}

📍 is-a 관계

  • 상속 관계 (부모-자식 관계)
  • ~는 ~이다.
  • 자동차는 탈것이다. = Car is a Vehicle.
  • 강아지는 동물이다. = Dog is a animal.
class Animal
{

}

class Dog: Aniaml 
{

}

📍 has-a 관계

  • 포함, 위임 관계 (멤버 변수로 포함)
  • 구성 관계 또는 집합 관계를 나타낸다.

예시
도서관은 책을 가지고 있다. = Library has a book.
거실은 소파를 가지고 있다. = Living room has a sofa.

🍀 has-a 관계 예제

  • 자동차가 라디오를 가지고 있다. = Car has a radio.
  • 라디오를 갖고 있고, 라디오를 켜고 끄는 경우.
  • 라디오를 작동하는 세부 방법은 각 객체에 위임
class Radio
{
	public void TurnOn(bool on)
    {
    	if (on)
        	Console.WriteLine("Radio On");
        else
        	Console.WriteLine("Radio Off");
    }
}

public class Car
{
	private Radio music;
    
    public Car()
    {
    	music = new Radio(); // Car has-a Radio
    }
    
    public void MusicOn(bool on)
    {
    	music.TurnOn(on); // 자식 객체(Radio)의 기능을 부모 객체(Car)에 위임
    }
}

🍀 has-a 관계 예제

Car 클래스

  • 에어컨과 라디오 클래스를 갖고 있음
  • 에어컨 온도를 조절하고 라디오를 작동시킴
  • 객체를 생성하면 에어컨과 라디오 객체 자동 생성
  • 자식 객체의 기능은 잘 모르므로 실제 구현은 위임
class Airconditioner
{
	public void Up() { temperature++; }
    public void Down() { temperature--; }
}

public class Car
{
	private Airconditioner aircon;
    public Car()
    {
    	aircon = new Airconditioner(); // Car has-a Aircon
    }
    
    public void TemperatureUp { aircon.Up(); }
    public void TemperatureDown() { aircon.Down(); }
}

public class CarHasATest
{
	public static Main()
    {
    	// 차를 생성할 때 동시에 라디오와 에어컨 생성
        Car c = new Car("Avante");
        // 라디오 On
        c.MusicOn(true);
        // 에어컨 온도 높인다
        c.TemperatureUp();
    }
}

쉽게 말해서 Car 클래스가 Radio 클래스 객체를 생성해서 사용하는 것을 의미한다.


📍 업캐스팅 (upcasting)

  • 자식 타입 → 부모 타입 자동 타입 변환
  • 자식 클래스의 레퍼런스 값을 부모 클래스 레퍼런스에 대입
    ✔ 부모 클래스가 자식 클래스 객체를 가리키게 되는 현상
    ✔ 객체 내에 있는 모든 멤버에 접근할 수 없고, 부모 클래스의 멤버에만 접근 가능하다.
  • 장점 : 부모 타입으로 묶어서 다룰 수 있다.
  • 단점 : 자식 고유의 멤버에 접근할 수 없다.
class Person
{
	class Student: Person
    {
    
    }
    
    Student student = new Student();
    Person person = student;
}
  • personStudent 객체를 참조하고 있지만, 타입은 Person이라서 Person에 정의된 멤버만 사용할 수 있다.
class Person
{
	protected string name;
    protected int id;
    
    public Person(string name)
    {
    	this.name = name;
    }
}

class Student: Person
{
	string grade;
    string dept;
    
    public Student(string name) : base(name) { }
    public static void Main(string[] args)
    {
    	Person person;
        Student student = new Student("Park");
        person = student; // 업캐스팅     
        Console.WriteLine(p.name); // 오류 X
    }
}
  • p.grade = "A" → 컴파일 오류
  • p.dept = "CS" → 컴파일 오류

📍 다운캐스팅 (downcasting)

  • 부모 타입 → 자식 타입 강제 타입 변환
  • 부모 클래스 레퍼런스를 자식 클래스 레퍼런스에 대입
  • 업캐스팅된 것을 다시 원래대로 되돌리는 것
  • 명시적으로 타입 지정
class Person
{

}

class Student: Person
{

}

Student student = new Student();
Person person = student; // 업캐스팅, 자동타입변환 
Studnet student = (Student)person; // 다운캐스팅, 강제타입변환
public static void Main(String[] args)
{
	Person p = new Student("Park"); // 업캐스팅
    Student s = (Student)p; // 다운캐스팅
    Console.WriteLine(s.name); // 오류 X
    
    s.grade = "A"; // 오류 X
    s.dept = "CS"; // 오류 X
    
}
  • 업캐스팅 하여 자식 멤버에 접근할 수 없을 때, 다시 자식 타입으로 돌려서 자식 기능을 쓰고 싶을 때 다운캐스팅 한다.

📍 오버라이딩

  • 부모 클래스에서 선언된 메소드를 자식 클래스에서 재구현(Override) 할 수 있다.
  • 실행 시간에 새로 정의된 Override된 멤버의 내용으로 처리한다.

📍 virtual 메서드

  • 부모 클래스의 virtual 메서드를 재정의하기 위해서 override 키워드를 사용한다. (선택적)
  • static, private과 함께 사용할 수 없다.
  • virtual 메서드와 override 메서드는 이름, 접근 제한자, 반환 값, 매개변수 리스트가 동일해야 한다.

virtual 메서드 정의

class Shape
{
	public string Name()
    {
    	...
    }
    
    public virtual void Draw()
    {
    	Console.WriteLine("그림을 그린다.);
    }
}

class Circle: Shape
{
	public override void Draw() 
    {
    	Console.WriteLine("원을 그린다.");
    }
}

🍀 virtual - override 예시

오버라이딩된 메서드가 항상 호출된다.

public class MethodOverridingEx
{
	public static void Main(string[] args)
    {
    	Shape shape = new Shape();
        Circle circle = new Circle();
        
        Shape a = new Circle(); // 업캐스팅
        Shape b = circle; 		// 업캐스팅
        
        shape.Draw(); // Shape Draw() 실행
        circle.Draw(); // Circle Draw() 실행
        
        a.Draw(); // 오버라이딩된 Circle Draw() 실행
        b.Draw(); // 오버라이딩된 Circle Draw() 실행
    }
}
  • Draw() 메서드는 virtual로 선언되어 있어서
    → 런타임(실행 시간)에 실제 객체 타입CircleDraw()가 실행된다.

📍 추상 클래스

  • 부모 클래스에서 abstract 키워드를 통해 정의한 메서드를 자식 클래스에서 override하여 재정의한다. (필수적)
  • 상속관계에서 가장 상위에 존재한다.
abstract class Shape 
{ 
	public abstract void Draw() { }
}

public class Circle: Shape
{
	public override void Draw() { }
}

class AbstractTest
{
	static void Main()
    {
    	Shape shape = new Circle();
        shape.Draw(); // Circle Draw()
        
        shape = new Square();
        shape.Draw(); // Square Draw()
    }
}

추상 메서드 정의

  • 추상 클래스만이 추상 메서드를 가질 수 있다.
  • 추상 메서드는 암시적으로 virtual 메서드이다.
  • virtual 키워드와 함께 사용할 수 없다.
  • 메서드 이름 앞에 abstract 키워드를 사용해야 한다.
  • 자식 클래스에서 반드시 재정의해야 한다.
abstract class Ticket
{
	public virtual string StartTime() { }
    public abstract int Fare(); // 강제성 메서드명만 정의
}

class BusTicket: Ticket
{
	public override string StartTime() { }
    public override int Fare() {} // 자식 클래스에서 선언
}

💡 추상 메서드 정리

  • 자식 클래스마다 목적에 맞게 추상 메서드를 다르게 구현할 수 있다.
  • 부모 클래스에서는 개념을 정의하고, 각 자식 클래스에서 구체적인 행위를 구현한다.

0개의 댓글