클래스 정의
클래스 정의 (Class Definitions)
클래스의 원형 선언은 다음과 같아요.
본문
Remark MacPas 모드에서는 Object 키워드가 class 키워드로 대체돼요. 이는 Mac에서 쓰이는 다른 파스칼 컴파일러와의 호환을 위한 거예요. 즉 MacPas 모드에서는 위 다이어그램의 예약어 class 대신 object를 쓸 수 있습니다.
클래스 선언에서는 필요한 만큼 가시성 블록을 사용할 수 있어요. 여러 블록을 반복해도 되고, 반드시 지켜야 할 순서도 없습니다. 메서드는 일반 함수나 프로시저 선언이에요. 보시다시피 클래스 선언은 객체 선언과 거의 동일해요. 객체와 클래스의 진짜 차이는 생성되는 방식에 있어요(이 장 뒷부분에서 다룹니다).
각 섹션의 가시성은 다음과 같아요.
- Private — private 블록의 모든 필드와 메서드는 클래스 정의를 담고 있는 모듈(유닛) 안에서만 접근할 수 있어요. 클래스의 메서드 안에서도, 바깥(예: 다른 클래스의 메서드)에서도 접근 가능합니다.
- Strict Private — strict private 블록의 모든 필드와 메서드는 클래스 자신의 메서드에서만 접근할 수 있어요. 다른 클래스나 자손 클래스(같은 유닛에 있어도)는 strict private 멤버에 접근할 수 없습니다.
- Protected — Private과 같지만, Protected 섹션의 멤버는 다른 모듈에 구현되어 있어도 자손 타입에게도 접근 가능합니다.
- Strict Protected — Protected와 같지만, 같은 유닛에 구현된 다른 클래스에게도 접근 가능하다는 차이가 있어요. strict protected 멤버는 자손 클래스에게만 보이고, 같은 유닛의 다른 클래스에게는 보이지 않습니다.
- Public — 항상 접근할 수 있어요.
- Published — 언어 관점에서는 Public과 같지만, 컴파일러가
{$M+}상태일 때 이 클래스의 자동 스트리밍(streaming)에 필요한 타입 정보도 함께 생성해요. published 섹션에 정의된 필드는 클래스 타입이어야 하고, 배열 프로퍼티는 published 섹션에 둘 수 없습니다.
문법 다이어그램에서 클래스가 구현하는 인터페이스를 나열할 수 있는 것도 볼 수 있어요. 이 기능은 다음 장에서 다룹니다.
클래스에는 **클래스 메서드(Class methods)**도 담을 수 있어요. 이는 인스턴스가 필요 없는 함수예요. 이런 메서드에서도 Self 식별자는 유효하지만, 클래스 포인터(VMT)를 가리킵니다.
Remark 함수나 포인터 타입처럼 클래스도 때로는 forward 정의가 필요해요. 클래스 forward 정의는 아래 예시처럼 클래스 이름과 Class 키워드만 적는 형태예요.
Type
TClassB = Class;
TClassA = Class
B : TClassB;
end;
TClassB = Class
A : TClassA;
end;
클래스 forward 정의를 쓸 때는, 그 클래스가 같은 유닛의 같은 섹션(interface/implementation)에 정의되어야 해요. 반드시 같은 type 섹션일 필요는 없습니다.
**클래스 참조 타입(Class reference types)**도 정의할 수 있어요. 클래스 참조 타입은 컴파일 타임에는 아직 알 수 없지만 런타임에 지정되는 특정 클래스의 인스턴스를 만드는 데 사용돼요. 본질적으로 클래스 참조 타입의 변수는 지정된 클래스의 정의에 대한 포인터를 담고 있습니다. 이를 이용해 해당 정의에 해당하는 클래스 인스턴스를 만들거나 상속 관계를 검사할 수 있어요. 아래 예시를 볼게요.
Type
TComponentClass = Class of TComponent;
Function CreateComponent(AClass: TComponentClass;
AOwner: TComponent): TComponent;
begin
// ...
Result:=AClass.Create(AOwner);
// ...
end;
이 함수에는 TComponent에서 파생된 어떤 클래스의 클래스 참조라도 넘길 수 있어요. 다음은 유효한 호출이에요.
Var
C : TComponent;
begin
C:=CreateComponent(TEdit,Form1);
end;
CreateComponent 함수가 돌아오면 C에는 TEdit 클래스의 인스턴스가 담겨 있어요. 반면 다음 호출은 컴파일되지 않습니다.
Var
C : TComponent;
begin
C:=CreateComponent(TStream,Form1);
end;
TStream은 TComponent에서 파생되지 않는데 AClass는 TComponent 클래스를 가리키거든요. 컴파일러는 이를 컴파일 타임에 확인해서 오류를 냅니다.
클래스 참조는 상속 관계 검사에도 쓰여요.
TMinClass = Class of TMyClass;
TMaxClass = Class of TMyClassChild;
Function CheckObjectBetween(Instance : TObject) : boolean;
begin
If not (Instance is TMinClass)
or ((Instance is TMaxClass)
and (Instance.ClassType<>TMaxClass)) then
Raise Exception.Create(SomeError)
end;
위 예시는 넘어온 인스턴스가 TMinClass의 자손이 아니거나 TMaxClass의 자손이면 예외를 발생시킵니다.
클래스 인스턴스화에 대한 자세한 내용은 6.4절(321쪽)에서 다뤄요.