할당 연산자
할당 연산자
Pascal에서 := 연산자는 한 타입의 변수 값을 다른 변수에 할당하는 동작을 규정해요. Free Pascal에서는 이 할당 연산자를 직접 정의해서, 새로 만든 타입에 맞는 할당 동작을 지정할 수 있습니다.
출처: 문서
본문
할당 연산자는 한 타입의 변수를 다른 변수에 할당하는 동작을 정의해요. 이때 결과 타입은 할당문 왼쪽에 있는 변수의 타입과 일치해야 하고, 할당 연산자의 단일 매개변수는 할당문 오른쪽에 있는 표현식의 타입과 같아야 합니다.
이 체계를 이용하면 새로운 타입을 선언하고, 그 타입에 맞는 할당을 정의할 수 있어요. 예를 들어 새로 정의한 Complex 타입에 값을 할당하려면:
Var
C,Z : Complex; // New type complex
begin
Z:=C; // assignments between complex types.
end;
다음과 같은 할당 연산자를 정의해야 합니다.
Operator := (C : Complex) z : complex;
이번에는 real 타입을 complex 타입에 할당하고 싶은 상황을 볼게요.
var
R : real;
C : complex;
begin
C:=R;
end;
이 경우에는 다음 할당 연산자를 정의해야 합니다.
Operator := (r : real) z : complex;
이 선언에서 볼 수 있듯이, 연산자 오른쪽에 real 표현식, 왼쪽에 complex 표현식이 올 때 := 연산자의 동작을 규정합니다. 구현 예시는 이렇게 할 수 있어요.
operator := (r : real) z : complex;
begin
z.re:=r;
z.im:=0.0;
end;
예시에서 볼 수 있듯이 결과 식별자(여기서는 z)는 할당 결과를 저장하는 데 쓰여요. Delphi 모드나 ObjFPC 모드로 컴파일할 때는 특별한 식별자 Result를 쓸 수 있는데, z 대신 사용할 수 있으므로 위 코드는 다음처럼 쓸 수도 있습니다.
operator := (r : real) z : complex;
begin
Result.re:=r;
Result.im:=0.0;
end;
할당 연산자는 한 타입을 다른 타입으로 변환하는 데에도 사용돼요. 컴파일러는 오버로드된 할당 연산자들을 차례로 살펴보면서 왼쪽·오른쪽 표현식의 타입과 일치하는 것을 찾아요. 만약 일치하는 연산자가 없다면 "type mismatch" 오류가 발생합니다.
주의사항: 교환 법칙이 성립하지 않아요
할당 연산자는 교환적(commutative)이 아니에요. 컴파일러는 두 인자의 역할을 절대 뒤집지 않습니다. 즉 위에서 정의한 할당 연산자가 있다고 해도, 다음은 불가능해요.
var
R : real;
C : complex;
begin
R:=C;
end;
반대 방향의 할당을 허용하려면 그 방향에 대한 할당 연산자를 따로 정의해야 합니다. (실수와 복소수 사이에서는 이렇게 되지 않아요.)
주의사항: 암시적 타입 변환
할당 연산자는 암시적 타입 변환에도 사용되기 때문에 원치 않는 결과가 생길 수 있어요. 다음 정의를 생각해 볼게요.
operator := (r : real) z : complex;
function exp(c : complex) : complex;
그런데 다음 할당은 type mismatch를 일으킵니다.
Var
r1,r2 : real;
begin
r1:=exp(r2);
end;
불일치가 생기는 이유는 이래요. 컴파일러가 complex 인자를 받는 exp 함수 정의를 만나면, r2를 암시적으로 complex로 변환해 그 exp 함수를 사용해 버려요. 그 함수의 결과는 complex라서 r1에 할당할 수 없으니 "type mismatch" 오류가 나는 거예요. 컴파일러는 올바른 인자를 가진 다른 exp를 더 찾아보지 않습니다. 이 특정 문제는 다음처럼 지정해서 피할 수 있어요.
r1:=system.exp(r2);
명시적 typecast를 할 때도 할당 연산자가 있으면 컴파일러가 암시적 변환을 시도해요. 즉 다음 코드는
Var
R1 : T1;
R2 : T2;
begin
R2:=T2(R1);
이런 연산자로 처리됩니다.
Operator := (aRight: T1) Res: T2;
그러나 Explicit 연산자를 정의하면, 컴파일러가 typecast를 만났을 때 그쪽을 대신 사용해요. 반대 방향은 성립하지 않아요. 즉 일반 할당에서는 컴파일러가 explicit 할당 연산자를 고려하지 않습니다.
다음 정의가 있다고 할게요.
uses
sysutils;
type
TTest1 = record
f: LongInt;
end;
TTest2 = record
f: String;
end;
TTest3 = record
f: Boolean;
end;
이제 할당 연산자를 만들 수 있어요.
operator := (aRight: TTest1) Res: TTest2;
begin
Writeln('Implicit TTest1 => TTest2');
Res.f := IntToStr(aRight.f);
end;
operator := (aRight: TTest1) Res: TTest3;
begin
Writeln('Implicit TTest1 => TTest3');
Res.f := aRight.f <> 0;
end;
typecasting 연산자도 정의할 수 있고요.
operator Explicit(aRight: TTest2) Res: TTest1;
begin
Writeln('Explicit TTest2 => TTest1');
Res.f := StrToIntDef(aRight.f, 0);
end;
operator Explicit(aRight: TTest1) Res: TTest3;
begin
Writeln('Explicit TTest1 => TTest3');
Res.f := aRight.f <> 0;
end;
그래서 다음 코드는
var
t1: TTest1;
t2: TTest2;
t3: TTest3;
begin
t1.f := 42;
// Implicit
t2 := t1;
// theoretically explicit, but implicit op will be used,
// because no explicit operator is defined
t2 := TTest2(t1);
// the following would not compile,
// no assignment operator defined (explicit one won't be used here)
//t1 := t2;
// Explicit
t1 := TTest1(t2);
// first explicit (TTest2 => TTest1) then implicit (TTest1 => TTest3)
t3 := TTest1(t2);
// Implicit
t3 := t1;
// explicit
t3 := TTest3(t1);
end.
다음 출력을 만들어 냅니다.
Implicit TTest1 => TTest2
Implicit TTest1 => TTest2
Explicit TTest2 => TTest1
Explicit TTest2 => TTest1
Implicit TTest1 => TTest3
Implicit TTest1 => TTest3
Explicit TTest1 => TTest3