10 장객체와클래스의고급기능 1
강의목표 불변클래스 (immutable class) 로부터불변객체생성 (10.2) #ifndef 포함감시지시자를사용한다중선언예방 (10.3) 인스턴스와정적변수, 함수의차이점이해 (10.4) 원하는동작을수행하기위한소멸자 (destructor) 구현 (10.5) 같은유형의다른객체로부터데이터를복사하는복사생성자를사용한객체생성 (10.6) 깊은복사 (deep copy) 를수행하기위한복사생성자지정 (10.7) 클래스의전용멤버로접근하기위한프렌드 (friend) 함수와프렌드클래스구동 (10.8) 합성관계를모델링하기위한클래스개발 (10.9-10.11) 크기변경이가능한배열로서 C++ vector 클래스사용 (10.12) 2
불변객체와클래스 일단객체가생성된이후에 ( 멤버끼리복사 (memberwise copy) 인경우를제외하고 ) 객체의내용이변경될수없다면, 그객체는불변객체 (immutable object) 라하며그객체의클래스를불변클래스 (immutable class) 라고부른다. 만약이전예제에서 Circle 클래스안의 set 함수를삭제한다면 radius 변수가전용 (private) 이되어 set 함수로만변경할수있게되므로클래스는불변상태가된다. - 기호는전용제어자 (modifier) 를나타냄. Circle -radius: double +Circle() +Circle(radius: double) +getradius(): double +setradius(radius: double): void +getarea(): double 원의반지름 ( 기본값 : 1.0) 기본원객체생성지정된반지름으로원객체를생성원의반지름값을반환원에대해새로운반지름값으로설정원의면적을반환 3
예 모두전용 (private) 데이터필드만갖고있으며어떠한변경자 (mutator) 도가지고있지않은클래스가반드시불변상태가되는것은아니다. 이를증명하기위해두개의클래스, Person 과 Date 를정의해보자. 그림 10.1 과 10.2 는이들두개의클래스에대한 UML 클래스다이어그램을보여주고있다. Person -id: int -birthdate: Date* +Person(id: int, year: int, month: int, day: int) +getid(): int +getbirthdate(): Date* 사람의 ID 사람의출생일지정된 ID, 년, 월, 일로 Person 생성사람의 ID 반환사람의출생일반환 4
예 Date -year: int -month: int -day: int +Date(newYear: int, newmonth: int, newday: int) +getyear(): int +setyear(newyear: int): void 날짜중년도날짜중월날짜중일지정된년, 월, 일로 Data 생성날짜중년도값반환날짜에대한새로운년도설정 5
예제코드 Person.h Person.cpp Date.h Date.cpp TestPerson Run 6
다중선언예방 일반적으로프로그램에서부주의로같은헤더파일을여러번포함시키는실수를많이하게된다. 예를들어, 리스트 10.6 에서처럼이프로그램에서 Date 가사용되기때문에 Date 클래스에대한헤더파일을추가할수있다. 그러면 Date 에대해다중선언이있다고하는컴파일오류가표시된다. TestPerson1 Date1 7
인스턴스와정적멤버 실체화 circle1 메모리 Circle radius = 1 numberofobjects = 2 1 radius -radius: double -numberofobjects: int +getnumberofobjects(): int +getarea(): double 실체화 circle2 2 numberofobjects UML 기호 + : 범용 (public) 변수나함수 - : 전용 (private) 변수나함수밑줄 : 정적변수나함수 radius = 5 numberofobjects = 2 5 radius Circle5.h Circle5.cpp TestCircle5.cpp Run 8
클래스이름사용 정적함수를호출하기위해서는 ClassName::functionName(arguments), 그리고 ClassName::staticVariable 을사용하여라. 이는사용자가클래스안의데이터와정적함수를쉽게인식할수있게함으로써가독성을높여준다. 9
인스턴스또는정적? 변수또는함수가인스턴스이어야하는지정적이어야하는지를어떻게결정할수있을까? 클래스의특정인스턴스에독립적인변수나함수는인스턴스변수나함수이어야한다. 클래스의특정인스턴스에독립적이지않은변수나함수는정적변수나함수이어야한다. 예를들어, 모든원은자신만의반지름을가지고있다. 반지름은특정원에독립적이다. 그러므로 radius 는 Circle 클래스의인스턴스변수가된다. getarea 함수가특정원에독립적이기때문에그것도인스턴스함수가된다. numberofobjects 는어떤특정인스턴스에대해서독립적이지않기때문에정적으로선언되어야한다. 10
소멸자소멸자 (destructor) 는생성자와반대개념이다. 생성자는객체가생성될때호출되지만소멸자는객체를없앨때호출된다. 소멸자를명시적으로선언하지않았다면모든클래스는기본소멸자 (default destructor) 를가지게되지만, 때로는특정동작을수행하기위한소멸자를만들수도있다. 소멸자는생성자에서와같은방법으로이름을주게되지만이름앞에틸드문자 (~) 를붙여야한다. 리스트 10.12 는소멸자가정의된 Circle 클래스이다. Circle6.h Circle6.cpp TestCircle6.cpp Run 11
복사생성자 각클래스에서는몇개의오버로드된생성자와하나의소멸자를정의할수있다. 부가적으로모든클래스는복사생성자 (copy constructor) 를가질수있다. 복사생성자는다음과같이표기한다. ClassName(ClassName &) 예를들어, Circle 클래스에대한복사생성자는 Circle(Circle &) 이다. 복사생성자는다른객체의데이터로초기화된객체를생성하는데사용할수있다. 기본적으로복사생성자는한객체에서의각데이터필드를다른객체에서의대응부분으로간단히복사한다. 리스트 10.15 에서이에대해설명한다. CopyConstructorDemo Run 12
얕은복사와깊은복사 객체를복사하기위한기본복사생성자 (default copy constructor) 또는대입연산자 (assignment operator) 는깊은복사 (deep copy) 보다는얕은복사 (shallow copy) 를수행하는데, 이는만약필드가어떤객체로의포인터라고한다면그포인터의내용이아닌주소가복사된다는것을의미한다. 리스트 10.16 에서이에대해설명한다. ShallowCopyDemo Run 13
얕은복사 person2 가 person1 으로복사되기전에는 person1 과 person2 의 birthdate 필드는두개의다른 Date 객체를가리킨다. person1: Person 메모리 id = 111 birthdate 111 참조 : BirthDate year = 1970 month = 5 day = 3 id = 222 birthdate person2: Person 메모리 222 참조 : BirthDate year = 2000 month = 11 day = 8 14
깊은복사 person2 가 person1 으로복사된후에는 person1 과 person2 의 birthdate 필드는같은 Date 객체를가리킨다. person1: Person 메모리 id = 222 birthdate 222 참조 : BirthDate year = 1970 month = 5 day = 3 id = 222 birthdate person2: Person 메모리 222 참조 : BirthDate year = 2000 month = 11 day = 8 15
복사생성자지정 이전절에서논의한것처럼기본복사생성자또는대입연산자 (=) 는얕은복사 (shallow copy) 를수행한다. 깊은복사 (deep copy) 를수행하기위해서복사생성자를사용하여구현할수있다. 리스트 10.17 은 7 번줄에서복사생성자를선언하기위해 Person 클래스에대한선언을수정한것이다. Person1.h CustomCopyConstructor Person1.cpp Run 16
프렌드함수와클래스 클래스의전용 (private) 멤버는클래스의밖에서접근할수없다. 종종어떤함수나클래스가클래스의전용멤버에접근할수있도록하는것이편리할때가있다. C++ 에서는프렌드 (friend) 함수와프렌드클래스를선언하기위해서 friend 키워드를사용할수있는데, 이로써함수와클래스가클래스의전용멤버에접근이가능하도록할수있다. 17
프렌드클래스 Date2.h TestFriendClass Run 18
프렌드함수 TestFriendFunction Run 19
객체합성 합성은실제로집합 (aggregation) 관계의특별한경우이다. 집합모델은 has-a 관계이며, 두객체사이의소유관계를나타낸다. 소유하고있는객체 (owner object) 를 aggregating 객체, 그객체의클래스를 aggregating 클래스라고한다. 종속되는객체 (subject object) 는 aggregated 객체, 그객체의클래스를 aggregated 클래스라고한다. 합성 집합 이름 1 1 학생 1 1..3 주소 20
집합관계는항상 aggregating 클래스에서데이터필드로서표현된다. 예를들어그림 10.6 에서의관계는다음과같이나타낼수있다 : class Name {... } class Student { private: Name name; Address address; class Address {... } }... Aggregated class 클래스 Aggregating class 클래스 Aggregated class 클래스 21
집합또는합성 집합과합성관계가같은방법으로클래스를사용하여표현될수있기때문에, 많은문헌에서그둘을구별짓지않고둘다모두합성이라고한다. 22
Course 클래스 Course -name: string -students: string[100] -numberofstudents: int +Course(name: &string) +getname(): string +addstudent(student: &string): void +getstudents(): string* +getnumberofstudents(): int 과정이름과정을듣는학생학생수 ( 기본 : 0) 지정한이름으로 Course 생성과정이름반환과정목록에새로운학생을추가과정에대한학생배열을반환과정에대한학생수를반환 Course.h Course.cpp TestCourse Run 23
StackOfInteger 클래스 스택은그림 10.10 에서설명하는바와같이 LIFO(last-in first-out) 방식으로객체를저장하는데이터구조이다. 이에대한많은응용이있는데, 예를들면컴파일러는함수호출을처리하기위해스택을사용한다. 함수가호출됐을때함수의매개변수와지역변수는스택안으로입력 (push) 된다. 함수가다른함수를호출하면새로운함수의매개변수와지역변수가스택안으로입력된다. 함수가자기할일을끝내고호출한곳으로반환될때함수와관련된공간이스택으로부터해제된다. 24
StackOfInteger 클래스 Data1 Data2 Data3 Data1 Data2 Data1 Data3 Data2 Data1 Data3 Data2 Data1 Data2 Data1 Data1 25
클래스 UML StackOfIntegers 클래스는스택을캡슐화하고스택을다루기위한동작을제공한다. StackOfIntegers -elements: int[100] -size: int +StackOfIntegers() +empty(): bool +peek(): int +push(value: int): int +pop(): int +getsize(): int 스택안에정수를저장하기위한배열스택안의정수의수빈스택생성스택이비어있다면참을반환스택상단의정수값을제거하지않고그값을반환스택제일위로정수값저장스택상단의정수값제거하고그값을반환스택안의요소의수를반환 26
클래스 UML elements[capacity 1] elements[size-1] elements[1] elements[0]...... 상단 하단 크기 용량 StackOfIntegers.h TestStackOfIntegers StackOfIntegers.cpp Run 27
C++ vector 클래스 이전의두예에서는학생과 int 값을저장하는데배열을사용했다. 하지만이는중요한제한요소가있었는데, 즉배열의크기를클래스선언에서고정시켜야했다. C++ 는 vector 라는클래스를제공하는데, 이는배열보다는더많은유연성을보여준다. 배열과똑같이 vector 객체를사용할수있는데, 벡터 (vector) 의크기는필요에따라자동적으로증가시킬수있다. 28
C++ vector 클래스 vector<datatype> +vector<datatype>() +push_back(element: datatype): void +pop_back(): void +size(): unsigned int +at(index: int): datatype +empty(): bool +clear(): void +swap(v2: vector): void 지정된요소유형으로빈벡터를생성벡터에요소를추가벡터로부터마지막요소를제거벡터안의요소의수를반환벡터안의지정인덱스에서의요소값을반환벡터가비어있다면참을반환벡터의모든요소를제거지정된벡터와현재벡터의내용을교환 TestVector Run 29