With 문
With 문
레코드나 객체, 클래스의 필드에 접근할 때마다 이름을 일일이 붙여야 하면 조금 번거롭죠. with 문은 그런 수고를 덜어주는 문이에요. 레코드, 객체, 클래스의 요소에 접근할 때 번거로운 이름 반복 없이 접근할 수 있게 해줘요.
본문
with 문의 문법은 이렇게 생겼어요.
with 변수참조 do 문장
여기서 변수 참조는 반드시 레코드, 객체, 클래스 타입의 변수여야 해요. with 문 안에서는 어떤 변수 참조나 메서드 참조가 등장하면, 먼저 그 참조가 레코드/객체/클래스의 필드나 메서드인지 검사해요. 만약 그렇다면 해당 필드에 접근하거나 해당 메서드를 호출해요.
다음과 같이 선언했다고 해볼게요.
Type
Passenger = Record
Name : String[30];
Flight : String[10];
end;
Var
TheCustomer : Passenger;
그러면 다음 두 문장은 완전히 동등해요.
TheCustomer.Name := 'Michael';
TheCustomer.Flight := 'PS901';
With TheCustomer do
begin
Name := 'Michael';
Flight := 'PS901';
end;
with 문은 여러 변수를 쉼표로 연결해서 한 번에 지정할 수도 있어요. 다음 문장은
With A,B,C,D do Statement;
아래의 중첩된 with와 동등해요.
With A do
With B do
With C do
With D do Statement;
이것은 변수들을 나중에 지정한 것부터 먼저 시도한다는 사실을 보여주는 좋은 예시예요. 즉 컴파일러가 변수 참조를 만나면, 가장 마지막에 지정된 변수의 필드/메서드인지 먼저 확인하고, 아니면 그 바로 전 변수, 이런 순서로 확인해요. 다음 예시가 이를 보여줘요.
Program testw;
Type AR = record
X,Y : Longint;
end;
PAR = ^Ar;
Var S,T : Ar;
begin
S.X := 1;S.Y := 1;
T.X := 2;T.Y := 2;
With S,T do
WriteLn (X,' ',Y);
end.
이 프로그램의 출력은 다음과 같아요.
2 2
즉 WriteLn 안의 X, Y가 T 레코드 변수에 대응한다는 뜻이에요.
주의할 점이 하나 있어요. 포인터나 클래스와 함께 with 문을 쓸 때는, with 블록 안에서 그 포인터나 클래스 자체를 변경하면 안 돼요. 앞의 예시에 이어서 다음 코드를 보면 무슨 뜻인지 알 수 있어요.
Var p : PAR;
begin
With P^ do
begin
// Do some operations
P:=OtherP;
X:=0.0; // Wrong X will be used !!
end;
포인터를 바꿀 수 없는 이유는, 컴파일러가 그 주소를 임시 레지스터에 저장해 두기 때문이에요. 포인터를 바꿔 봤자 그 임시 주소는 바뀌지 않아요. 클래스의 경우도 마찬가지예요.
더 알아보기
- 레코드(record) 타입과 객체, 클래스의 기본 개념을 먼저 살펴보면 좋아요.
with문 안에서 이름 충돌이 일어날 때의 동작도 이해해 두면 유용해요.