객체지향언어로서의오브젝트파스칼 (Object Pascal As A OOP) 오브젝트파스칼의객체지향성을몰라도델파이어플리케이션을쉽게만들수있다. 단순히폼을하나만들고, 거기에여러가지컴포넌트들을추가하고, 이벤트핸들러에적당한내용의코드들을추가하면그걸로충분한것이다. 그렇지만, 이것을이해하면델파이가어떤방법으로작업을처리하는지이해할수있고, 자신만의컴포넌트를만들어내거나, 비교적커다란프로젝트를진행할때에커다란도움을받게될것이다. OOP 의기본개념 프로그램은데이터와이를처리하는알고리즘으로구성되어있다고하는유명한말이있다. 그렇기때문에, 프로그래밍언어의구조를가만히살펴보면대부분의경우데이터를다루는데필요한각종정의들과, 이들을제어하기위한여러가지문법으로구성되어있다는것을알수있다. 기본적으로데이터의표현은 데이터형 (data type) 이라는개념으로표현되는데, 이데이터형의개념이발전하면서언어들이다른모습을띠게된다. 초기의프로그래밍언어는정해진데이터형을이용하였으며, 그이후의프로그래밍언어 ( 파스칼등 ) 에서는레코드나배열등의개발자가정의해서사용할수있는데이터형을도입하였다. 그러다가, 이러한데이터형에그데이터를다룰때쓰이는작업과결부시키게되었는데, 이와같이기본적인데이터와이를다루기위한메쏘드로이루어진데이터형인클래스가 OOP 의기초가되는개념이다. 데이터형과함께가장중요한프로그래밍언어의구성요소인제어구조를보면, 초기의프로그래밍언어는점프 (goto) 와분기문 (if-then, case 등 ), 순환문 (for, while 등 ) 으로이루어진제어문을가지고있었다. 여기에서브루틴의개념이추가되어일반적인프로그래밍언어의전반적인구조를가지게되었다. OOP 에서는이러한서브루틴을데이터와마찬가지로추상화하여, 이들을클래스로관리하게된다. OOP 에있어서가장중요한 3 가지개념은캡슐화, 상속, 다형성으로아래에간단하게설명하였다. 캡슐화 (Encapsulation) OOP 의가장핵심적인요소라고할수있는것이캡슐화개념으로, 데이터와서브루틴을 추상화한것이다. 이개념은클래스로구현되는데, 클래스란특정객체그룹을추상적으로 정의해놓은것이라고생각하면된다. 클래스는일종의형 (type) 의정의로서필드 ( 클래스의
객체상태를나타내는데이터 ) 가있고, 이에대한작업을정의하는메소드가있다. 여기서혼동하지말아야할것은클래스란일종의데이터형이고, 객체는클래스라는데이터형인하나의구체적인예 ( 인스턴스 ) 라는것이다. 예를들어설명하면클래스란객체를만들기위한주형이며, 객체란클래스라는주형에서만들어진병과같은것이다. 상속 (Inheritance) 상속은클래스를처음부터만들지않고, 기존의클래스를기반으로해서새로운클래스를정의하는것이다. 이때기반이되는클래스를부모클래스, 만들어진클래스를서브클래스라고하며서브클래스는부모클래스로부터필드와메소드를상속한다. 상속개념이실제로쓰일때에는다음과같은특징을가지고있다. 1. 상속은일반적인클래스의특수한경우를나타낼때쓰인다. 즉, 자동차라는부모클래스에서기본적인자동차의필드와메소드를표현했다고하면, 여기에서상속받은티코라는서브클래스는기본적인자동차클래스의필드와메소드외에자신만의필드와메소드를정의할수있다. 2. 반대로여러가지로특화된클래스들의공통점을가진부모클래스를만들수도있다. 에를들어, 기존에학생이라는클래스와선생이라는클래스를가지고있는경우이들의공통적인필드와메소드를정의하는사람이라는부모클래스를만들고, 공통의요소를공유하게할수있다. 3. 실제로상속개념이쓰일때에는중복되는코드를막을수있으며 ( 공통부분을가지는부모클래스가사용되므로 ), 복잡한데이터의개념을쉽게이해할수있게된다. 다형성 (Polymorphism) 다형성이란하나의프로그램변수를가지고서로다른클래스객체를참조하는것을말한다. 즉, 어떤객체에대해작업을할때객체의 type 에적절하게반응할수있다는의미이다. 클래스와객체 (Classes and Objects) 델파이 OOP 에대한문법적인고찰 구체적인예제에들어가기전에, 델파이의 OOP 에서나오게되는중요한개념들과문법적 인특징에대해서알아보도록하자. 1. 필드 (Fields)
필드는객체에속해있는일종의변수라고이해하면된다. 필드는클래스형을포함해서어떤데이터형으로도선언해사용할수있다. 필드를선언하려면단순히변수를선언하듯이하면된다. 예를들어, 다음의선언부는 TNumber 라는컴포넌트를선언하는데, 여기에는 Int 라는정수필드만을가지고있다. type TNumber = class Int: Integer; 2. 메소드 (Methods) 메소드는클래스와연관되어있는프로시저나함수를말한다. 메소드를호출할때에는반드시객체를지정해야하며, 메소드는그객체위에서동작한다는점이일반적인프로시저나함수와의차이점이다. 예를들어다음의코드를살펴보자. SomeObject.Free; 이코드는 SomeObject 라는객체의 Free 메소드를호출한다. 3. Inherited 키워드 메소드를구현할때 inherited 라는키워드를사용하면클래스의조상이가지고있던메소드를호출하게된다. 보통메소드를오버라이드할때, 그메소드의기능을추가하기위해일단은조상클래스의메소드를호출하고나서부가적인기능을수행하거나, 사전작업을지정한후마지막에조상클래스의메소드를 inherited 키워드를이용하여호출하는것이전형적인방법이다. 4. Self 지시어 메소드의구현부분에 Self 라는지시어를사용하면, 이지시어는메소드가실행되는객체를지칭하는것이다. 예를들어, 다음의코드는 TCollection 클래스의 Add 메소드를구현한부분이다. function TCollection.Add: TCollectionItem;
Result := FItemClass.Create(Self); 여기에서 Add 메소드는 FItemClass 필드에의해참조되는 Create 메소드를호출하는데, 이필드는항상 TCollectionItem 의자손이다. TCollectionItem.Create 메소드는 TCollection 형의파라미터를하나가지기때문에, Add 가호출될때 TCollection 인스턴스객체를넘겨준다. 5. 프로퍼티 (Properties) 프로퍼티는필드와마찬가지로, 객체의속성을정의하는것이다. 그렇지만필드가단지내용이변경되거나, 검사할때사용되는단순한저장소의역할을하는데비해프로퍼티는데이터를읽고, 쓰는특별한동작과연관되어있다. 프로퍼티는객체의속성에접근할수있는제어권을제공한다. 프로퍼티에대한보다자세한내용은제 4 부의내용을참고하기바란다. 클래스의선언, 생성, 파괴 클래스는사용자가정의한데이터형이다. 클래스는내부적인데이터와메소드를가지고있으며, 유사한많은객체들의전반적인특징과행동들을정의한다. 이에비해객체는클래스의구체적인변수로, 실제로메모리를차지하게되는인스턴스이다. 오브젝트파스칼에서새로운클래스데이터형을선언하는구문은다음과같다. type TPerson = class Name, ID: string; 위의코드는 TPerson 이라는클래스를선언하고, 이클래스의필드인 Name, ID 를정의했다. 델파이에서는일반적으로모든클래스의이름앞에는 T 자를붙이도록되어있다. 이것은강제사항이아니지만관습을따르는것이아무래도좋을것이다. 위의선언문을보면마치레코드선언과비슷하다는것을알수있을것이다. 클래스의상속을하는구문은 class 키워드옆에괄호를치고이안에상속받을클래스의이름을적어넣으면된다. TPerson = class(tobject)
라는구문은 TObject 라는클래스에서상속받는 TPerson 클래스를선언하는구문이다. 이들에접근하기위해서는다음과같은방법을이용하면된다. var Person: TPerson; Person.Name := 정지훈 ; Person.ID := ttolttol 그러나, 위의코드는실제로실행되지않는다. 이는 TPerson 이라는클래스의변수인 Person 을선언했지만, 실제이클래스의인스턴스가생성되지않았기때문이다. cf. 오브젝트파스칼에서는클래스형으로선언된각변수들이각각의객체의값을가지고있는것이아니라, 객체에대한참조값 (reference), 즉그객체가저장된메모리위치의포인터를가지고있는것이다. 이러한패러다임을 객체참조모델 (object reference model) 이라고한다. 그러므로아래와같이변수를선언하면, var Person: TPerson; 메모리에객체가만들어지는것이아니라그저객체에대한메모리의위치가정해지고, 여기에대한참조값이 Person 변수에저장되는것이다. 그러므로, 실제로객체의인스턴스를사용하려면인스턴스를직접만들어야한다. 폼에추가하는컴포넌트의인스턴스는델파이에의해자동으로만들어진다. 객체의인스턴스를만들기위해서는그객체의생성자 (constructor) 인 Create 메소드를사용하면된다. 생성자는새로운객체를위해메모리배치를하고초기화시키는특수한프로시저이다. 이러한생성자는델파이의객체모델의가장기본적인클래스인 TObject 에서상속받게되므로, 개발자들은아무걱정없이이를사용할수있다. 아래의코드를보자. var Person: TPerson;
Person := TPerson.Create; Person.Name := 정지훈 ; Person.ID := ttolttol ; TPerson.Create 구문에의해실제객체가생성된다. 이 Create 메소드는 TObject 클래스의 constructor 로서, 모든클래스가이를상속하게된다. 그러므로, TPerson = class 와 TPerson = class(tobject) 는같은의미이다. 이렇게일단객체를생성했으면이를결국에는삭제해야한다. 이때에는 Free 메소드를호출하면된다. Free 메소드역시 TObject 클래스의메소드이다. 이와같이필요할때객체를만들어서사용하고일이끝나면없애버리는식으로사용하면된다. 그럼, 이를이용해서간단한예제를만들어보도록하겠다. 다음과같이에디트박스 2 개와버튼 4 개를폼위에올려놓고, 각버튼의 Name 프로퍼티를 btncreate, btnfree, btnassign, btnshow 로설정하자 ( 그림 5-1). 그리고, Caption 프로퍼티를 생성, 해제, 대입, 보기 로설정한다. 각에디트박스의 Text 프로퍼티는지운다. 유닛의 type 문장에 TPerson 클래스를선언하고, 전역변수 Person 을선언한다. 그리고, 각버튼의 OnClick 이벤트핸들러를아래와같이작성한다. type ( 중략 ) TPerson = class // 클래스선언부 Name, ID: string; var Form1: TForm1; Person: TPerson; // 전역변수선언 procedure TForm1.btnCreateClick(Sender: TObject); Person := TPerson.Create; // 인스턴스생성 procedure TForm1.btnAssignClick(Sender: TObject); Person.Name := Edit1.Text; // 에디트박스의값을대입
Person.ID := Edit2.Text; procedure TForm1.btnShowClick(Sender: TObject); ShowMessage (' 안녕하세요? '+Person.Name+ ' 씨, 당신의 ID 는 '+Person.ID+' 입니다.'); procedure TForm1.btnFreeClick(Sender: TObject); Person.Free; ( 그림 5-1) 예제폼그림 4 개의버튼에대한이벤트핸들러를이와같이작성하면 생성 버튼을클릭하면 TPerson 클래스의인스턴스가생성되어 Person 변수에대입되고, 대입 버튼을클릭하면 Person 객체의 Name, ID 필드에에디트박스의내용이대입된다. 보기 버튼을클릭하면 Person 객체의 Name, ID 필드를이용해다음 ( 그림 5-2) 과같은메시지박스가뜬다. 이때 TPerson 클래스의인스턴스가생성되지않았다면 Access violation 에러메시지가나타날것이다 ( 그림 5-3). 마찬가지로 생성 버튼을클릭한후, 해제 버튼을클릭하면생성되었던 TPerson 클래스의인스턴스가파괴되므로, 이때 대입, 보기 버튼을클릭하면역시 Access violation 에러가발생한다. ( 그림 5-2) 에디트박스이름 ( 정지훈 ) 과 ID(ttolttol) 를입력한후 생성, 대입, 보기 버튼을클릭하
면나타나는메시지박스 ( 그림 5-3) Create 하지않았거나, Free 를호출한후 대입, 보기 버튼을클릭해서인스턴스에접근 하려하면이와같은에러메시지가나타난다. 메소드와생성자 (constructor), 파괴자 (destructor) 의추가 이제 TPerson 클래스에몇가지메소드를추가해서조금은쓸모가있는클래스로만들어보자. 현재가지고있는 Name 필드는그대로두고, ID 필드대신, 주민등록번호를저장할수있는 RegID 필드를만들자. 이때필드임을나타내기위해각필드의앞에접두어로 F 를붙이도록한다. 그리고, 주민등록번호필드값을가지고이값이유효한지알아보는 IsValid 함수와나이와성별을알수있는 GetAge, GetSex 라는함수를추가한다. 또한, 이클래스의객체가생성될때값을초기화시키기위해생성자를추가하자. 생성자 (constructor) 는특별한형태의프로시저로, 이것을클래스에적용하면자동적으로그클래스의객체를위해메모리를할당하게되며, 초기화작업을지정할수있다. 단순히 constructor 라는예약어로선언하면되며, 클래스메소드로사용할때에는객체에대한메모리할당작업을하게되지만, 이미인스턴스화된객체에서사용될때에는메모리할당작업은하지않고, 정의된초기화작업만하게된다. 여기서만드는클래스에는생성자로 Create 라는프로시저를정의하는데, 파라미터로 FName, FRegID 필드를채울수있도록선언한다. 마찬가지로파괴자 (destructor) 도정의할수있으며 destructor 라는예약어로선언하면된다. 이프로시저는객체가파괴되기전에시스템자원을 release 하는작업을주로하게되는데, 여기서는특별히추가하지않는다. 따로파괴자를추가하지않아도모든클래스는 TObject 에서파생되기때문에기본적인 Free, Destroy 프로시저는정의되어있다.. 이렇게확장한클래스의선언부는다음과같다. type TSex = (smale, sfemale); //GetSex 함수에서쓰임 TPerson = class(tobject) FName: string;
FRegID: string; constructor Create(Name, ID: string); function IsValid: Boolean; function GetSex: TSex; function GetAge: integer; 이제각함수를구현해보도록하자. 먼저생성자를구현해보자. 생성자에서는 Name, ID 를파라미터로받아서이값을필드에 적용하여초기화한다. constructor TPerson.Create(Name, ID: string); FName := Name; FRegID := ID; 다음으로 FRegID 필드에저장된주민등록번호가잘못된것이아닌지검사하는 IsValid 함수를구현하도록하자. 주민등록번호를검증하는방법은주민등록번호의 13 자리중마지막자리를제외한 12 자리에각각 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5 를곱한뒤전체를더한다. 예를들어, 701203-1079727 이라는주민등록번호가있다고하면, (7*2) + (0*3) + (1*4) + (2*5) + (0*6) + (3*7) + (1*8) + (0*9) + (7*2) + (9*3) + (7*4) + (2*5) = 136 이다. 이값을 11 로나눈나머지값을 11 에서뺀다. 위의경우에는나머지가 4 이므로 11-4=7 이된다. 이값이마지막주민등록번호값과일치하면일단유효한번호인것이다. 일종의해싱함수 (hashing function) 이다. 이를이용해서 IsValid 함수를구현해보자. function TPerson.IsValid: Boolean; Result := False; if ((Length(FRegID) = 13) and ( 0000000000000 < FRegID) and ( 9999999999999 > FRegID)) then if (11 - ((StrToInt(FRegID[1]) * 2 + StrToInt(FRegID[2]) * 3 + StrToInt(FRegID[3]) * 4 + StrToInt(FRegID[4]) * 5 + StrToInt(FRegID[5]) * 6 + StrToInt(FRegID[6]) * 7 + StrToInt(FRegID[7]) * 8 + StrToInt(FRegID[8]) * 9 + StrToInt(FRegID[9]) * 2 + StrToInt(FRegID[10]) * 3 + StrToInt(FRegID[11]) * 4 + StrToInt(FRegID[12]) * 5) mod 11)) =
StrToInt(FRegID[13]) then Result := True else Result := False; 즉, 일단 FRegID 필드값이 13 자리이고, 숫자로이루어졌는지확인하고, 각자리의값을 정수형으로바꿔서위에서설명한공식에따라적절한지알아보고적절하면 True, 그렇지 않으면 False 를반환한다. Cf. 1. 파스칼의문자열은일종의배열로생각할수있기때문에, 이런형태의코드가가능한것이다. 예를들어 Name 이라는문자열변수의값이 Sample 이라고할때, Name[1], Name[2], Name[3], Name[4], Name[5] 의값은각각 S, a, m, p, l, e 이다. 2. Length 함수문자열의길이를구하는함수이다. 델파이 1.0 까지는문자열의 0 번째요소, 즉위의예를들면 Name[0] 값에문자열의길이가저장되어있었으나, 2.0 부터는 Length 함수를사용하여길이를구한다. 3. StrToInt, IntToStr 함수정수와문자열의형전환을해주는함수이다. 예를들어 IntToStr(123) 의결과값은 123 이고, StrToInt( 123 ) 의결과값은 123 이다. 마지막으로 GetSex 와 GetAge 를구현해보자. function TPerson.GetSex: TSex; if IsValid then if FRegID[7] = '1' then Result := smale else Result := sfemale; function TPerson.GetAge: integer; var Date: string; if IsValid then
Date := DateToStr(Now); Result := StrToInt(Copy(Date, 1, 2)) - StrToInt(Copy(FRegID, 1, 2)); if (Copy(Date, 4, 2) + Copy(Date, 7, 2)) < Copy(FRegID, 3, 4) then Result := Result - 1; GetSex 함수는주민등록번호의 7 번째자리가 1 이면남자, 2 이면여자이므로쉽게이해가될것이다. GetAge 함수는일단 System 의날짜를얻을수있는 Now 함수를사용한후이를문자열로변환한다. 그리고, 여기서연도만을뽑아정수로변환한후, 주민등록번호의첫두자리가태어난해를가리키게되므로이를정수로변환해서뺀다. 여기에생일이지나지않았으면 1 을빼야만으로계산한나이가된다. 생일을비교할때, 입력받은주민등록번호상의생일은 0301 과같이연속된 4 개의문자로구성되지만, DateToStr 로변환된 Date 변수의값은 98-06-21 과같은형식으로저장되기때문에이를동등하게비교하기위해 Copy 함수를이용하여주민등록번호상의생일처럼연속된 4 개의문자로만드는루틴이추가되었다. 그럼, 이클래스를활용하는프로그램을만들어보자. 두번째예제에 TPerson 의구현부분을추가하고, 폼을다음 ( 그림 5-4) 과같이디자인한다. 여기서도첫번째예제와마찬가지로에디트박스를 2 개추가한다. 여기에는이름과주민등록번호를입력받을것이다. 그리고, 버튼을 2 개추가한후 Name 프로퍼티를 btnassign 과 btnexecute 로설정하고, Caption 을각각 대입 과 실행 으로설정한다. ( 그림 5-4) 두번째예제의폼디자인 그리고, 각버튼의이벤트핸들러를다음과같이작성한다. procedure TForm1.btnAssignClick(Sender: TObject); if Assigned(Person) then Person.Destroy; Person := TPerson.Create(Edit1.Text, Edit2.Text);
procedure TForm1.btnExecuteClick(Sender: TObject); var Sex, Age: string; if not Assigned(Person) then ShowMessage(' 클래스가생성되지않았습니다!'); Exit; if not Person.IsValid then ShowMessage(' 주민등록번호가유효하지않습니다 '); Exit; end else if Person.GetSex = smale then Sex := ' 남자 ' else Sex := ' 여자 '; ShowMessage(Person.FName + ' 씨, 당신은 ' + Sex + ' 이며, 만 ' + IntToStr(Person.GetAge) + ' 세입니다.'); 그렇게어렵지않은코드이므로자세한설명은생략하도록하겠다. 참고로제대로된주민등록번호를입력했을때와그렇지않았을때의메시지박스는다음과 같다. ( 그림 5-5) 예제실행결과 이런클래스의사용방법은델파이에서 TComponent 를클래스를상속해서더욱재사용성이뛰어난컴포넌트로개발할수있다. 이를위해서는프로퍼티에대한이해와델파이가제공하고있는컴포넌트모델에대한이해가필요한데, 여기에대해서는제 4 부에서자세
하게다룰것이다. 상속성과델파이폼 새로운프로젝트를생성하면, 델파이는새로운폼을보여준다. 코드에디터의새로운프로젝트의내용을살펴보면, 델파이가폼에대한새로운객체클래스를선언하고새로운폼객체를생성하는코드를만들어낸다는것을알수있다. 그러면초기에생성되는델파이의코드를살펴보자. unit Unit1; interface uses Windows, Classes, Graphics, Forms, Controls,... ; type TForm1 = class(tform) { 폼에대한새로운클래스선언 } private { Private declarations } public { Public declarations } { 클래스선언부는여기서종료된다.} var Form1: TForm1; implementation { 구현부분의시작 } {$R *.DFM} end. { 구현부분과유닛의끝 } 새로운객체데이터형인 TForm1 은 TForm 에서상속받는다는것을알수있다. 객체에 는데이터필드와메소드를가진다는것은이미설명한바있다. 그런데, TForm1 에는아
직메소드나데이터필드를지정하지않았다. 다만 TForm 클래스에서상속받은메소드와프로퍼티를가지게될것이다. 개발자는 TForm1 의선언부분에마음대로메소드나프로퍼티등을추가할수있다. 또한, 컴포넌트를폼에추가하는동작에의해델파이가메소드나데이터필드를추가해준다. 그렇지만, 이어플리케이션을실행시켜보면간단한폼이생성되는것을볼수있다. 이것이의미하는것은무엇인가? 즉, 기본적으로 TForm 클래스의모든메소드와기능을상속받아서사용할수있다는것이다. 변수선언부에서는새로운변수인 Form1 을 TForm1 으로선언한다. 이렇게선언함으로써 TForm1 클래스의인스턴스가될수있다. 클래스의인스턴스는여러개생성될수있다는것은이미알고있을텐데, 만약 TForm1 클래스를여러인스턴스로생성하면그것이바로 MDI(Multiple Document Interface) 어플리케이션이되는것이다. 그러면, 폼에버튼을하나추가하고그버튼의 OnClick 이벤트핸들러를다음과같이작성하자. procedure TForm1.Button1Click(Sender: TObject); Form1.Color := clgreen; 이렇게이벤트핸들러를작성하고나면, 폼의유닛의코드는다음과같이바뀌어있을것이 다. unit Unit1; interface uses Windows, Classes, Graphics, Forms, Controls,...; type TForm1 = class(tform) Button1: TButton; { 새로운데이터필드 } procedure Button1Click(Sender: TObject); { 새로운메소드 } private { Private declarations } public { Public declarations }
var Form1: TForm1;... ( 후략 ) TForm1 의선언부에새로운 Button1 이라는필드가추가되었음을알수있다. 이렇게새로운컴포넌트를폼에추가할때마다새로운필드가 type 선언부에추가된다. 또한, 델파이에서작성하는모든이벤트핸들러는폼객체의메소드로선언된다. TForm1 에는이제 Button1Click 이라는새로운메소드프로시저가추가되었다. 여기서이런동작들을 OOP 의개념으로생각해보자. 델파이의폼디자이너는과연무엇인가? 결국델파이의폼디자이너는 TForm 이라는클래스를상속받은새로운형태의클래스를쉽게만들어주는일종의위저드인셈이다. 이제상속의의미를이해하기쉬운비유를들어생각해보자. 앞에서처음어플리케이션이생성되었을때의폼은기본옵션으로된자동차를한대구입한것으로가정하자. 기본옵션의자동차는자동차메이커에서항상정해진방식대로만들어지기때문에가장기본적인기능만을가지고달릴수있을것이다. 그렇지만, 이자동차를구입한사람이에어컨도달고, 파워핸들과에어백등의여러가지옵션을장착할수있다. 이를위해서자동차를구입한사람은자동차전체를새로만들필요는없는것이다. 즉, 이를상속을통해설명하자면기본적인자동차를상속받은사용자가자식클래스자동차에새로운옵션들을추가한것이다. 마찬가지로델파이의폼은상속성을설명할때가장알기쉽고, 전형적인방법을보여준다고말할수있다. 클래스의범위 (scope) 클래스의멤버들은서로다른범위를가질수있다. 이러한범위를나타내는지시어로는 private, protected, public, published 의 4 가지가있다. 참고로오브젝트파스칼에서는유닛도범위를결정하는데한몫을한다. 비록 private 로선언되었더라도같은유닛에있으면모두접근이가능하다. private 다른유닛에있는경우 private 섹션에선언된멤버에는접근할수없다. 다른사용자 가접근할필요가없는멤버들은여기에선언한다.
protected 이클래스에서상속받은클래스에서만접근할수있는멤버들을여기에선언한다. 즉, 현재의클래스를상속받아서새로운클래스를만들때수정이필요하다면개발자에게접근이가능해야하지만, 일반적으로사용할때에는접근할수없도록할때사용된다. public, published 다른클래스에서제한없이사용될수있는멤버들을여기에선언한다. published 는오브젝트인스펙터에서도볼수있는멤버들을선언할때사용한다. published 로선언하면멤버에대한 RTTI(runtime type information) 가생성되며, 이를이용해서다른프로그램들이런타임에서객체에대한필드, 메소드, 프로퍼티에접근하게된다. 델파이는 RTTI 를이용하여오브젝트인스펙터에프로퍼티를보여주며, 폼파일에프로퍼티의값을저장하고불러올수있다. Published 프로퍼티에사용할수있는데이터형은서수형과문자열, 클래스와메소드포인터형, 세트형, 실수형등을사용할수있다. 배열은사용할수없다. published 멤버가있는대부분의클래스는 TPersistent 클래스에서상속받는것이보통이다. 범위에대한지시어가없을때에는일단 public 으로간주한다. 그렇지만, 프로그래밍을할 때에는꼭이러한범위를지정해주는것이좋은버릇이다. 메소드 (Method) 객체의동작은메소드에의해정의된다. 메소드는프로시저나함수와비슷하지만지정한클래스와그의파생클래스만의객체를위해정의된다는점이다르다. 메소드에는보이지않는파라미터로서 Self 라는객체자신의참조자가전달된다. 메소드는또한클래스메소드로서선언될수있는데, 이런경우에는클래스참조로는호출될수있으나객체참조로서는호출되지않는다. 객체가없으므로 Self 파라미터도없다. 메소드의종류 메소드에도그동작방식과용도에따라여러가지종류가있다. 에대해설명하였다. 다음에이들각각의특징 1. 정적메소드 (Static method): 지시어 static; 아무런지시어가없을때에는디폴트로정적메소드로간주된다. 컴파일할당시에메 소드가위치한메모리주소가확정되는메소드이므로, 그만큼실행속도가빠르지만상
속을받은클래스에서메소드를새롭게정의하면, 그메소드만 ( 같은이름의경우 ) 사용 이가능하므로융통성이적다. 2. 가상메소드 (Virtual mehtod): 지시어 virtual; 가상메소드는실행시에 late binding 이라는과정을통해실제로호출될메모리주소가결정된다. 내부적으로가상메소드가참조되면변수에의해참조되는객체의실제클래스형이사용되는데, 이작업이가상함수테이블 (virtual method table(vmt), vtable) 에기록되어있는주소를참조하여이루어진다. 그러므로, 실제참조되는클래스형에따라서여러가지메소드가호출될수있는 다형성 이구현될수있다. 3. 동적메소드 (Dynamic method): 지시어 dynamic; 기본적인사용법이나목적은가상메소드와동일하지만, 내부적인처리방법에약간의차이가있다. 가상메소드가내부적으로 VMT 를이용해서메모리주소를참조하는데비해동적메소드는메소드를지정하는코드를이용해서메모리주소를찾게된다. 그렇기때문에가상메소드처럼테이블을직접참조하는방법보다다소느리게동작하지만메모리는덜사용하게된다. 4. 추상메소드 (Abstract method): 지시어 abstract; 일반적으로메소드를클래스에선언할때, 컴파일러는메소드프로시저가유닛의어느부분인가구현되어있을것으로간주하게된다. 그런데, 추상메소드로선언하면컴파일러는구현부분이자손클래스에있을것으로생각하고이를검사하지않는다. 그런데, 만약자손클래스에서이를구현하지않아서추상메소드가호출되면치명적인에러가발생하게되므로주의하기바란다. 메소드오버라이드 (method override) override 지시어는가상또는동적메소드를재정의할때사용된다. 재정의하는방법은다 음과같다. type TMyParent = class procedure AMethod; virtual; TMyClass = class(tmyparent)
procedure AMethod; override; 클래스메소드 (Class method) 보통메소드는클래스의인스턴스에대한행동을정의한다. 그런데, 어떤때에는클래스자체에대한메소드가있으면할경우가있다. 이럴때에는클래스메소드를정의해서사용한다. 클래스메소드는객체가생성되지않아도사용할수있다. 클래스메소드를선언하려면메소드정의부분에서 class 키워드를앞에붙여주면된다. 메소드오버로딩 델파이 4 에서는객체들이같은이름을가진여러개의메소드를가질수있다. 이를메소드오버로딩이라고하는데, 같은이름을가진메소드들은 argumets 의 type signature 를가지고서로를구별한다. 오버로드된메소드는키워드오버로드로표시된다. 그렇기때문에, 객체들은다음과같이다른두개의생성자를가질수있다. constructor Create(AOwner: TComponent); overload; override; constructor Create(AOwner: TComponent; Text: string); overload; 전역함수와프로시저역시오버로드가가능하다. 델파이 3 까지만해도메소드오버로딩을지원하지않았기때문에, 생성자의이름을다르게했어야했다. 예를들어모든윈도우컨트롤은 Create, CreateParented 의 2 개의생성자를공통적으로가지고있었다. 델파이 4 부터는 Create 라는메소드이름을가진여러생성자를가질수있다. 오브젝트파스칼의이전버전에서는같은이름의메소드를선언할경우조상클래스의메소드는사용되지않았다. 예를들어, Create 메소드를 Owner 파라미터를넘겨주지않고호출할경우과거에는 TObject 에선언된생성자를호출하지않고컴파일에러를발생시켰다. 메소드오버로딩으로이러한문제들이다소변경되었는데, 다음의코드를살펴보자. type A = class public procedure p(i: Integer); virtual; B = class(a)
public procedure p(s: string); var ab: B; ab.p('one'); { works } ab.p(1); { compile error! } 여기에서변경된메소드의파라미터를대입할경우에는동작하지만, 원본클래스의메소드 는동작하지않는다. 이를해결하기위해서는 overload 지시어를사용하면된다. 다음의 코드를살펴보자. type A = class public procedure p(i: Integer); overload; virtual; B = class(a) public procedure p(s: string); overload; var ab: B; ab.p('one'); { works } ab.p(1); { now this one works too! } 이경우에는동작하지만, 컴파일러가경고를한다. 에다음과같이 reintroduce 키워드를지정하면된다. 이를없애기위해서는상속받은메소드 type
A = class public procedure p(i: Integer); overload; virtual; B = class(a) public procedure p(i: Integer; S: string); reintroduce; overload; 동적바인딩 (Dynamic binding) 과다형성 (Polymorphism) 파스칼의함수와프로시저는기본적으로정적바인딩 (static binding) 을이용하고있다. 이것은메소드호출이컴파일러나링커에의해해석되며, 컴파일러나링커는이호출문을그함수나프로시저가존재하는특정메모리위치의호출로바꾸어놓는다는의미이다. 오브젝트파스칼을비롯한객체지향언어는이와다른형태의동적바인딩을지원한다. 이경우에는메소드의실제메모리주소가실행중에결정되는것이다. 이런특성을이용해다형성을지원할수있게되는데, 다형성이란어떤메소드의호출문을작성하고그것을변수에대입해도, 어느메소드가호출될지는그변수에관계된객체의데이터형에따라달라지는것이다. 다시말해, 주어진메소드에대해다수의버전이있을수있고, 그래서하나의메소드가이러한버전들각각을가리킬수있다. 정리 (Summary) 이번장에서는오브젝트파스칼의 OOP 적인특성에대해간단하게나마알아보았다. 사실오브젝트파스칼의기능을충분히활용하려면 OOP 에대한전반적인이해가필수적이다. 이책의범위가 OOP 에대해심도있게논의할수는없지만, 이책을읽는독자들은반드시따로 OOP 에대해공부해서그기법과철학을체득하길바란다. 그렇게익힌 OOP 에대한개념들은실제로델파이프로젝트를진행하게되면소중한지식으로활용될것이다. 다음장에서는 OOP 의대표적인언어인 C++ 과자바를오브젝트파스칼과비교하는시간을가지도록한다. 서로다른언어의특징에대해서알아보는것도델파이를잘이해하는데큰도움이될것이다.