최초 객체지향 프로그래밍(OOP)이 소개되었을 때, 터보 파스칼(TP)은 record 를 확장한 object 형을 사용하였습니다. 현재 델파이의 경우 class 를 주로 사용하지만 여전히 object 형을 지원하고 있으며 이는 프리 파스칼(FPC) 도 마찬가지입니다. 다음은 레퍼런스 문서에 있는 내용을 제 개인적 기준으로 요약 정리해 보았습니다.^^ 매뉴얼에서는 멤버 변수를 field 로 지칭하고 있습니다.
1. Declaration
type
TObj = object
private
Caption: ShortString;
public
constructor Init;
destructor done;
procedure SetCaption(AValue: String);
function GetCaption: String;
end;
private 영역의 경우 델파이와 마찬가지로 같은 유닛 내에서는 friendly 관계입니다. 참고로 class 에서는 strict private 와 같은 문법이 보강되었습니다.(MacPas 모드에서 맥의 여타 파스칼과 호환을 위하여 object 키워드가 class 키워드로 대체되었습니다. 즉, MacPas 모드에서는 오브젝트형을 사용할 수 없습니다.)
FPC 에서는 packed record 처럼 packed object 를 사용할 수 있습니다.
2. Fields
레코드와 유사하며 사용법도 비슷합니다.
type
TAnObject = object
AField: LongInt;
procedure AMethod;
end;
var
AnObject: TAnObject;
AnObject.AField := 0;
Self : 자신의 인스턴스를 가리킵니다.
procedure TAnObject.AMethod;
begin
...
Self.AField := 0;
...
end;
with 문의 경우...
with AnObject do
begin
AField := 12;
AMethod;
end;
3. Static fields
{$STATIC ON} 이 선언되면, 오브젝트는 static field 를 가질 수 있습니다. 흔히 알고 있는 클래스의 static field 를 생각하면 됩니다. 물론, 델파이에서는 비교적 후에 추가(class var)되었지만...^^ (이 녀석이 없어서 Singleton 패턴 구현시 전역 변수를 사용하거나 클래스 내에 const 로 선언한 변수의 포인터를 사용하는 등... 편법을 동원하곤 했습죠. 과거의 이야기네요. ㅎ)
{$static on}
type
TCL = object
L: LongInt; static;
end;
var
CL1, CL2: CL;
begin
CL1.L := 2;
WriteLn(CL2.L);
CL2.L := 3;
WriteLn(CL1.L);
WriteLn(TCL.L);
end.
결과는 다음과 같습니다.
2
3
3
4. constructors and destructors
오브젝트가 virtual 메소드를 사용해야한다면 constructor 와 destructor 가 쌍으로 필요합니다. virtual 메소드를 사용하면 몇 가지 내부적으로 정리해야할 부분(VMT의 초기화)이 생기기 때문입니다.
내부적으로는 이렇게 선언됩니다.
constructor init(_vmt : pointer; _self : pointer ...);
destructor done(_vmt : pointer; _self: pointer ...);
확장된 문법으로 New 와 Dispose 함수를 제공하는데 스택이 아닌 힙(동적 메모리)에 인스턴스를 생성하게 됩니다. 이때 생성자(constructor)의 이름도 함께 사용됩니다.
type
TObj = object;
constructor Init;
...
end;
PObj = ^TObj;
var
PP: PObj;
begin
PP := New(PObj, Init);
또는
New(PP, Init);
또는
New(PP);
PP^.Init;
end;
마지막의 경우 오브젝트의 인스턴스를 사용하여야한다는 경고가 발생할 수 있습니다. 힙에 생성된 오브젝트를 해제하기 위하여 Dispose 함수를 사용하며, 마찬가지로 소멸자(destructor)의 이름이 함께 사용됩니다.
5. methods
뭐... 일반적인 클래스 사용법이랑 동일합니다. Self 도 마찬가지구요.
5.1 declaration
TP 나 델파이의 경우, field 는 메소드의 앞에 선언되어야 합니다. 하지만 FPC 는 상관없습니다. 아래 코드는 컴파일시 TP 와 델파이에서 오류가 발생하지만 FPC 에서는 발생하지 않습니다.
type
MyObj = object
procedure DoIt;
Field: LongInt;
end;
5.2 method invocation
static methods
abstract 또는 virtual 이 선언되지 않은 메소드입니다.
type
TParent = object
...
procedure DoIt;
...
end;
PParent = ^TParent;
TChild = object(TParent)
...
procedure DoIt;
...
end;
PChild = ^TChild;
상기 소스를 다음과 같이 실행해보면...
var
ParentA, ParentB: PParent;
Child: PChild;
begin
ParentA := New(PParent, Init);
ParentB := New(PChild, Init);
Child := New(PChild, Init);
ParentA^.DoIt; // TParent 의 DoIt 이 호출
ParentB^.DoIt; // TParent 의 DoIt 이 호출
Child^.DoIt; // TChild 의 DoIt 이 호출
클래스에서 다형성을 말하는 것이구요. 이 경우는 메소드가 컴파일시 결정되기 때문에 override 되지 않는 현상입니다.(다형성 X)
virtual methods
TParent 와 TChild 의 DoIt 메소드에 virtual 을 모두 선언하면,
procedure DoIt; virtual;
결과가 다음과 같이 바뀝니다.(다형성 O)
ParentA^.DoIt; // TParent 의 DoIt 이 호출
ParentB^.DoIt; // TChild 의 DoIt 이 호출
Child^.DoIt; // TChild 의 DoIt 이 호출
제 개인적으로는 이러한 다형성이 결국 객체지향 프로그램의 핵심이라고 생각합니다. 상속 또한 다형성을 위해 사용된다고 할까요? 초기 OOP 가 등장했을 때는 상속이 최고인줄 알았지만 말입니다. ^^
아, 그리고 클래스에서처럼 inherited 사용이 가능합니다.(예전 TP 에선 이런거 없었는데 말이죠.)
procedure TChild.DoIt;
begin
inherited DoIt;
...
end;
inherited 는 virtual 메소드에서만 사용이 가능합니다. VMT 와 관련하여, FPC 프로그래머 가이드에서 좀 더 자세한 내용을 확인할 수 있습니다.
인스턴스는 이렇게 메모리에 생성되고...
VMT 는 이렇게 생성됩니다.
abstract methods
추상 메소드는 virtual 메소드의 일종입니다. 델파이와 달리 abstract 메소드가 선언된 놈을 인스턴스화하려고하면 컴파일시 에러가 발생합니다. 사실 델파이만 좀 특이한거죠.^^
6. visibility
private, protected, public 은 클래스와 동일합니다.
이상 오브젝트형을 정리해 보았는데요. 과거 터보 파스칼 시절이 새록새록 떠오르네요. Turbo Vision 의 프리파스칼 버전인 Free Vision 의 경우, 이 오브젝트형을 적절히 사용하고 있습니다. 그래서 이 놈을 살펴봤네요. ^^
'프로그래밍 > PC' 카테고리의 다른 글
XE6, 파이어몽키 그리고 TWideMemoField 버그 수정 (2) | 2014.06.30 |
---|---|
FPC 로 OS 커널 만들기... - 3 - (0) | 2014.04.30 |
FPC 로 OS 커널 만들기... - 2 - (0) | 2014.04.11 |
FPC 2.6 지원 CPU 및 OS (0) | 2014.04.10 |
FPC 로 OS 커널 만들기... - 1 - (1) | 2014.04.09 |
댓글