지난 시간에 델파이의 클래스에 대해서 정리했다. 클래스는 재사용이 가능하다는 장점이 있고, 상속이라는 특징을 가지고 있다. 상속 관련된 몇 가지 키워드가 있는데, 오늘은 그 키워드들을 정리해보고자 한다.


먼저 Virtual은 부모 클래스에서 상속가능한 것임을 명시하는데 사용된다. 


자식클래스에서는 Override키워드를 통해 재정의하거나 해당 기능을 사용하는 것을 의미하며


Inherited는 Override를 통해 재정의를 하는 도중에 부모의 기능을 그대로 사용하고자 하는 부분에 쓴다.


Reintroduce는 상속받았지만 부모의 기능을 사용하지 않는다는 의미를 가진다.



음. 말로 써놓으니 하나도 모르겠군. 찬찬히 다시 한 번 보자. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type
  TIntroduce = class
    procedure WhoIAm; virtual;
  end;

  TChildIntroduce = class(TIntroduce)
    procedure WhoIAm; override;
  end;


procedure TIntroduce.WhoIAm;
begin
   TempMemo.Lines.Add('I AM TIntroduce');
end;

procedure TChildIntroduce.WhoIAm;
begin
  inherited;
 
  TempMemo.Lines.Add('I AM TChildIntroduce');
end;


//procedure TChildIntroduce.WhoIAm;
//begin
//  TempMemo.Lines.Add('I AM TChildIntroduce');
// 
//  inherited;
//end;
cs
<코드A>


위의 간단한 예제를 보자. TIntroduce라는 클래스를 선언하였고, 현재 이 클래스는 아무것도 상속받지 않은(실제로는 디폴트로 TObject클래스를 상속받게된다) 상태이다. 클래스 함수로는 WhoIAm라는 함수를 가지고 있으며, virtual이므로 이 클래스를 상속받는 클래스들은 WhoIAm함수를 재정의해주어야 한다.

아래 TChildIntroduce클래스는 TIntroduce클래스를 상속받고 있다. 그렇기 때문에 위에서 virtual키워드를 써준 WhoIAm함수를 재정의하고 override키워드를 써주었다. 

실제 클래스 함수 구현 부분을 보자. 11라인의 WhoIAm함수는 TIntroduce클래스에 속한 함수임을 알 수 있다. 함수의 내용은 'I AM TIntroduce'라는 문자열을 출력하는 것이다. 반면 16라인의 WhoIAm함수는 TChildIntroduce클래스에 속한 함수이며, 함수의 내용은 inherited키워드를 통해서 부모의 함수(상속받은 같은 이름의 그 함수)를 먼저 수행한 뒤에, 'I AM TChildIntroduce'를 출력해주는 것이다. 24라인의 WhoIAm함수의 경우 바로 위의 WhoIAm함수와 동일하지만 inherited의 위치가 16라인의 것과 다른 것을 볼 수 있다.

16라인의 WhoIAm함수와 24라인의 WhoIAm함수는 각각 어떻게 Memo에 출력하게될까? 

앞에서 inherited는 해당 함수를 상속받은 부모클래스에서 해당 함수를 호출하는 것과 같은 의미라고 하였다. 즉, 


1
2
3
4
5
6
7
8
9
10
11
12
procedure TIntroduce.WhoIAm;
begin
   TempMemo.Lines.Add('I AM TIntroduce');
end;


procedure TChildIntroduce.WhoIAm;
begin
  inherited;
 
  TempMemo.Lines.Add('I AM TChildIntroduce');
end;
cs

<코드B>


1
2
3
4
5
6
7
8
9
10
11
procedure TIntroduce.WhoIAm;
begin
   TempMemo.Lines.Add('I AM TIntroduce');
end;

procedure TChildIntroduce.WhoIAm;
begin
  TempMemo.Lines.Add('I AM TIntroduce');
 
  TempMemo.Lines.Add('I AM TChildIntroduce');
end;
cs

<코드C>


위의 <코드B>와 <코드C>는 같은 의미의 코드가 된다.



그럼 다시 <코드A>로 돌아가보자. 16라인과 24라인의 WhoIAm함수는 각각 어떻게 TempMemo에 출력이 될까. 먼저 16라인의 WhoIAm함수는 inherited로 부모 클래스의 해당 함수(TIntroduce.WhoIAm)를 호출하고나서 자신의 함수(TChildIntroduce.WhoIAm)를 호출하게 되므로 


1
2
I AM TIntroduce
I AM TChildIntroduce
cs
<코드A - 16라인의 WhoIAM>



반면 24라인의 WhoIAm함수는 자신의 함수(TChildIntroduce.WhoIAm)를 호출하고나서 inherited로 부모 클래스의 해당 함수(TIntroduce.WhoIAm)를 호출하게 되므로 아래와 같이 출력된다.

1
2
I AM TChildIntroduce
I AM TIntroduce
cs
<코드A - 24라인의 WhoIAm>









여기까지는 무리없이 이해가 되었겠지! 그럼 Override와 Reintroduce에 대해 알아보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
type
  TIntroduce = class
    procedure Create;
  end;

  TChildIntroduce1 = class(TIntroduce)
    procedure Create; override;
  end;

  TChildIntroduce2 = class(TIntroduce)
    procedure Create; reintroduce;
  end;

procedure TIntroduce.Create;
begin
   TempMemo.Lines.Add('I AM TIntroduce');
end;

procedure TChildIntroduce1.Create;
begin
  TempMemo.Lines.Add('I AM Chiled1');
end;

procedure TChildIntroduce2.Create;
begin
  TempMemo.Lines.Add('I AM Chiled2');
end;

procedure Test;
var
  aIntro1 : TIntroduce;
  aIntro2 : TIntroduce;
begin
  aIntro1 := TChildIntroduce1.Create;
  aIntro2 := TChildIntroduce2.Create;
end;
cs

<코드X>


오...코드가 너무 길지만 알고나면 너무 쉬운 소스니까 걱정말고 계속 읽어도 된다. 뒤로가기 없기~


우선 보면 TChildIntroduce1, 2는 TIntroduce를 상속받아서 생성한 클래스이다. 그리고 TChildIntroduce1은 상속받은 Create함수에 override키워드를, TChildIntroduce2는 reintroduce 키워드를 사용하였고, Test프로시저에서 aIntro1과 2를 TIntroduce 자료형으로 선언한 뒤에, 각각 TChildIntroduce1과 2로 Create해주었다. 


어떤 메시지를 출력하게 될까?


구글링을 좀 해본 사람은 바인딩(Binding)이라는 용어를 종종 보았을 것이다. override는 binding이 가능해서 자식의 메소드를 사용하고, reintroduce는 binding이 되지않아서 부모의 메소드를 사용하게 된다. 이 뜻을 이해하기 쉽게 그림을 준비했다. 





쨘! 세상에 단번에 이해가 갈듯. 후후후. 그림은 TParent와 그것을 상속받은 TChild1, TChild2를 나타내고 있다. TParent자료형으로 변수를 선언한 경우 노란색으로 표시한 부분(부모의 영역)만 접근하여 사용이 가능하게 된다. 그러나 TChild1의 경우 override를 이용해서 자식의 영역에 접근하여 자식의 선언을 이용할 수 있게 된다. 이것을 binding이라고 하는 것이다! 그럼 오른쪽의 TChild2의 경우 당연히 reintroduce로 선언한 것으로, binding이 되지 않기 때문에 자식의 영역에 접근이 불가능하며, 부모의 것을 그대로 사용하게 된다.



그럼 다시 <코드X>로 돌아가보자. aIntro1과 aIntro2는 똑같이 TIntroduce 자료형으로 선언되었다. 그리고 각각 TChildIntroduce1과 TChildIntroduce2로 객체가 생성되었다. aIntro1의 경우 override로 정의된 TChildIntroduce1으로 객체를 생성하였으므로, TIntroduce로 선언하였지만 자식의 영역에 접근이 가능할 것이고,


1
I AM Child1

cs


위와 같이 "I AM Child1"이라는 문구를 출력하는 자식의 Create문을 실행할 것이다.


반면에 aIntro2의 경우 reintroduce로 정의된 TChildIntroduce2로 객체를 생성하였으므로, 자식의 영역에 접근이 불가능하게 된다. 그러므로 


1
I AM TIntroduce
cs

위와 같이 "I AM TIntroduce"라는 문구를 출력하는 부모의 Create문을 실행하게 된다.





즉, 상속을 받아서 부모의 함수명을 그대로 쓰지만, 다르게 정의하고 싶을 때 아예 연관짓고 싶지 않을 때 reintroduce키워드를 사용하게 된다. 





되게 오래걸리네 이 포스팅! 이거 열심히 포스팅하다가 과제를 받았고,,,이제 과제의 늪으로 빠져야겠다. 앗 벌써 퇴근 30분전이라니!




내용이 잘못되거나 이해 안 되는 점은 댓글주세요


+ Recent posts