4. 클래스의완성 정보은닉 ( 자료은폐 ) 과캡슐화 생성자 소멸자 클래스와배열 자기참조 (this 포인터 ) friend 선언 Jong Hyuk Park
정보은닉과캡슐화 Jong Hyuk Park
3 장의내용정리 클래스에대한기본 (7 장까지이어진다 ) 무엇인가를구현하기에는아직도무리! 클래스의등장배경 현실세계를모델링 데이터추상화 클래스화 객체화 접근제어 : public, private, protected 3
4 장의핵심내용 클래스디자인기본원칙 캡슐화, 정보은닉 캡슐화와정보은닉의유용성 클래스객체의생성과소멸 생성자, 소멸자 생성자, 소멸자의유용성 4
정보은닉 (Information Hiding) 정보은닉의필요성 프로그램의안정적인구현과관련 (0, 0) (100, 100) class Point Public: int x; int y; * Point 마우스의위치정보를저장하기위한클래스 5
정보은닉 (Information Hiding) 6 /* InfoHiding1.cpp */ #include<iostream> using std::cout; using std::endl; using std::cin; class Point public: int x; // x 좌표의범위 : 0 ~ 100 int y; // y 좌표의범위 : 0 ~ 100 int main() int x, y; cout<<" 좌표입력 : "; cin>>x>>y; Point p; p.x=x; p.y=y; 실행후, 정상적인값입력 : 10 20 cout<<" 입력된데이터를이용해서그림을그림 "<<endl; return 0; 비정상값입력 : -20 120 모두정상적으로그림을그림 ------------------------------------ 문제 : 객체외부에서직접접근하는것 ----------------------------------- 간접접근으로수정?? InfoHiding2.cpp
정보은닉예 - InfoHiding2 / 3.cpp 7 #include<iostream> using std::cout; using std::endl; using std::cin; class Point // default private int x; // x 좌표의범위 : 0 ~ 100 int y; // y 좌표의범위 : 0 ~ 100 public: int GetX() return x; int GetY() return y; void SetX(int _x); void SetY(int _y); 실행 -10-100 여전히정상적인값이출력 ------------------------------------ 정보은닉이되었으나여전히문제 ----------------------------------- 데이터의경계검사와그에따른적절한처리부분 정보는은닉되었으나간접적인경로가없음 경로설정 void Point::SetX(int _x) if(_x<0 _x>100) cout<<"x 좌표입력오류, 확인요망 "<<endl; return; x=_x; void Point::SetY(int _y) if(_y<0 _y>100) cout<<"y 좌표입력오류, 확인요망 "<<endl; return; y=_y; int main() int x, y; cout<<" 좌표입력 : "; cin>>x>>y; Point p; p.setx(x); p.sety(y); cout<<" 입력된데이터를이용해서그림을그림 "<<endl; return 0;
캡슐화 (Encapsulation) 캡슐화 (Encapsulation) 의기본개념 관련있는데이터와함수를하나로묶는것 예 ) Encapsulation1.cpp Encapsulation2.cpp 캡슐화의유용성 생각해봅시다. P 132, Y2K 전역변수의사용시, 변경 관련된모든함수의수정이이루어져야함 하나의클래스내의변수를조작하기위한함수는다른클래스에두지않는것이바람직!! 8
Encapsulation1.cpp 캡슐화예 (1) 9 #include<iostream> using std::cout; using std::endl; using std::cin; class Point int x; // x 좌표의범위 : 0 ~ 100 int y; // y 좌표의범위 : 0 ~ 100 public: int GetX() return x; int GetY() return y; void SetX(int _x); void SetY(int _y); void Point::SetX(int _x) if(_x<0 _x>100) cout<<"x 좌표입력오류, 확인요망 "<<endl; return; x=_x; void Point::SetY(int _y) if(_y<0 _y>100) cout<<"y 좌표입력오류, 확인요망 "<<endl; return; y=_y; class PointShow public: void ShowData(Point p) cout<<"x 좌표 : "<<p.getx()<<endl; cout<<"y 좌표 : "<<p.gety()<<endl; int main() int x, y; cout<<" 좌표입력 : "; cin>>x>>y; Point p; p.setx(x); p.sety(y); PointShow show; show.showdata(p); return 0;
Encapsulation2.cpp #include<iostream> using std::cout; using std::endl; using std::cin; class Point int x; // x 좌표의범위 : 0~100 int y; // y 좌표의범위 : 0~100 public: int GetX() return x; int GetY() return y; 캡슐화예 (2) void Point::ShowData() cout<<"x 좌표 : "<<x<<endl; cout<<"y 좌표 : "<<y<<endl; int main() int x, y; cout<<" 좌표입력 : "; cin>>x>>y; void SetX(int _x); void SetY(int _y); void ShowData(); // 캡슐화를위해추가된함수. void Point::SetX(int _x) if(_x<0 _x>100) cout<<"x 좌표입력오류, 확인요망 "<<endl; return; x=_x; void Point::SetY(int _y) if(_y<0 _y>100) cout<<"y 좌표입력오류, 확인요망 "<<endl; return; y=_y; 10 Point p; p.setx(x); p.sety(y); p.showdata(); return 0;
정보은닉과캡슐화 객체지향프로그래밍의객체정보은닉, 캡슐화지원 오류검출등유지보수용이 정보은닉 ( 자료은폐, information hiding) 객체내부의자료를객체외부로부터은폐하여외부에서접근금지 객체내부자료를 private으로선언하여실현 필요성 : 프로그램의안정적구현 캡슐화 (Encapsulation) 관련된자료와함수를하나의단위로그룹화 11
생성자 Jong Hyuk Park
생성자 (constructor) 생성자의필요성 객체를생성과동시에초기화하기위해 객체는생성과동시에초기화되는것이좋은구조 생성자란? 객체생성시반드시한번호출되는함수 클래스와같은이름의함수 리턴형이없으며리턴하지도않음 13
생성자 (constructor) class Square public: int side; Square(); side = 0; // 생성자 void set(int); int area(); Square a; // 객체 a 선언, 생성자호출 class Square int side; public: Square(int n); side = n; // 생성자 void set(int); int area(); Square a(12); // 객체 a 선언, 생성자호출 14
생성자와소멸자 /* Person1.cpp */ class Person char name[size]; char phone[size]; int age; public: void ShowData(); void Person::ShowData() cout<<"name: "<<name<<endl; cout<<"phone: "<<phone<<endl; cout<<"age: "<<age<<endl; Private 접근 오류의원인은? 15 int main() Person p="kim", "013-113-1113", 22 p.showdata(); return 0; C 의구조체변수에서사용 C++ 본예제에서는직접접근 오류
생성자와소멸자 16 /* Person2.cpp */ class Person char name[size]; char phone[size]; int age; public: void ShowData() 생략 void SetData(char* _name, char* _phone, int _age); void Person::SetData(char* _name, char* _phone, int _age) strcpy(name, _name); strcpy(phone, _phone); age=_age; int main() Person p; p.setdata("kim", "013-333-5555", 22); return 0; name phone age showdate() setdata() 메모리공간 생성과동시에 초기화? X
생성자와소멸자 객체의생성과정 (Constructor1.cpp) 첫째. 메모리할당 둘째. 생성자의호출 class AAA int i, j; public: AAA() // 생성자 cout<<" 생성자호출 "<<endl; i=10, j=20; void ShowData() cout<<i<<' '<<j<<endl; 원하는값으로 초기화할수있는가? X main() AAA a; a i=? 10 j=? 20 AAA() showdate() 메모리공간 17
생성자와소멸자 생성자를통한인자의전달 이전의객체생성방법은구조체변수의선언과동일한방법! Constructor2.cpp, Person3.cpp class AAA int i, j; public: AAA(int _i, int _j) // 생성자 i=_i, j=_j; void ShowData() cout<<i<<' '<<j<<endl; 객체생성문법 aaa i=111 j=222 AAA() showdate() 18
생성자와소멸자 public 생성자, private 생성자 public 생성자 : 어디서든객체생성가능 private 생성자 : 클래스내부에서만가능 객체생성의두가지형태 Person p = Person("KIM", "013-333-5555", 22); Person p("kim", "013-333-5555", 22); 오해하기쉬운객체생성 Person p("kim", "013-333-5555", 22); // 객체생성 Person p( ); // 객체생성? 19
생성자와소멸자 디폴트 (default) 생성자 생성자가하나도정의되어있지않은경우 자동으로삽입이되는생성자 디폴트생성자가하는일? 아무것도없다. Default1.cpp 20
생성자와소멸자 생성자의특징 생성자도함수다! 따라서함수오버로딩이가능하다 디폴트매개변수의설정도가능하다. Default2.cpp, Default3.cpp 디폴트매개변수에의해 21
생성자와소멸자 객체가소멸되는시점은? 기본자료형변수, 구조체변수가소멸되는시점과동일하다. 함수내에선언된객체 함수호출이끝나면소멸된다. 전역적으로선언된객체 프로그램이종료될때소멸된다. 이렇게객체생성할일 ( 거의 ) 없다! 22
생성자와소멸자 생성자와동적할당 (Person4.cpp) class Person char *name; char *phone; int age; public: Person(char* _name, char* _phone, int _age); void ShowData() 생략 동적할당에따른메모리의해제는? Person::Person(char* _name, char* _phone, int _age) name=new char[strlen(_name)+1]; strcpy(name, _name); phone=new char[strlen(_phone)+1]; strcpy(phone, _phone); age=_age; 23
생성자와소멸자 메모리를해제하는멤버함수의제공 Person5.cpp class Person char *name; char *phone; int age; public: Person(char* _name, char* _phone, int _age); void DelMemory(); Person::Person(char* _name, char* _phone, int _age) 생략 만족하는가? NO 객체의소멸직전에동적할당을명시적으로해주어야함 부담. 24 void Person::DelMemory() delete []name; delete []phone;
생성자예 (1) #include <iostream> using std::cout; using std::endl; class Square int side; public: Square(int n); side = n; // 생성자 25 void set(int); int area(); void Square::set(int x) side = x; int Square::area() return side*side; int main() Square a(12); cout << "area : " << a.area() << endl; return 0;
#include<iostream> using std::cout; using std::endl; 생성자예 (2) class Point int x, y; public: Point(int _x, int _y) x=_x, y=_y; void ShowData() cout<<x<<' '<<y<<endl; int main() Point p1(10, 20); //10과 20을인자로받는생성자호출 p1.showdata(); Point p2; => 에러! p2.showdata(); return 0; 26
#include<iostream> using std::cout; using std::endl; 생성자예 (3) 생성자오버로딩 class Point int x, y; public: Point() x=y=0; Point(int _x, int _y) x=_x, y=_y; void ShowData() cout<<x<<' '<<y<<endl; 27 int main() Point p1(10, 20); //10과 20을인자로받는생성자호출 p1.showdata(); Point p2; //void 생성자호출. p2.showdata(); return 0;
생성자예 (4) 생성자디폴트매개변수 #include<iostream> using std::cout; using std::endl; class Point int x, y; public: Point(int _x=0, int _y=0) x=_x, y=_y; void ShowData() cout<<x<<' '<<y<<endl; int main() Point p1(10, 20); p1.showdata(); Point p2; // Point(0, 0) 과같다. p2.showdata(); Point p3(10); // Point(10, 0) 과같다. p3.showdata(); return 0; 28
생성자예 (4) 동적할당 #include<iostream> using std::cout; using std::endl; class Person char *name; char *phone; int age; public: Person(char* _name, char* _phone, int _age); void ShowData(); void DelMemory(); Person::Person(char* _name, char* _phone, int _age) name=new char[strlen(_name)+1]; strcpy(name, _name); phone=new char[strlen(_phone)+1]; strcpy(phone, _phone); age=_age; void Person::ShowData() cout<<"name: "<<name<<endl; cout<<"phone: "<<phone<<endl; cout<<"age: "<<age<<endl; 29 void Person::DelMemory() delete []name; delete []phone; int main() Person p("kim", "013-333-5555", 22); p.showdata(); p.delmemory(); return 0;
소멸자 Jong Hyuk Park
소멸자의특성과필요성 31 소멸자는생성자와반대로객체가선언되어사용되는블럭이끝날때 ( 객체가소멸될때 ) 자동적으로호출되는함수 소멸자는주로객체가생성될때동적으로할당한메모리를객체의소멸과더불어해제하고자할때사용 소멸자는클래스이름앞에 ~(tilde) 기호를붙여서정의 리턴하지않으며, 리턴타입없음 전달인자항상 void 오버로딩, 디폴트매개변수의선언불가능. 객체의소멸순서 첫째 : 소멸자호출 둘째 : 메모리반환 ( 해제 ) Destructor.cpp Person6.cpp
생성자와소멸자 디폴트 (Default) 소멸자 객체의소멸순서를만족시키기위한소멸자 명시적으로소멸자제공되지않을경우자동삽입 디폴트생성자와마찬가지로하는일없다! 32
생성자와소멸자 소멸자의명시적제공 생성자에서메모리동적할당을하는경우 Debugging 코드의작성 Cout 등객체소멸시점확인가능 33
#include <iostream> using std::cout; using std::endl; 소멸자예 (1) class Point int xval, yval; public: Point(int x, int y) xval = x; yval = y; ~Point() cout << "Destructing..\n"; // 소멸자 showxy() cout << xval << ", " << yval << endl; int main() Point pt1(9,4); // 내부블럭시작 Point pt2(11,88); pt2 : 11, 88 Destructing.. pt1 : 9, 4 Destructing.. 34 cout << "pt2 : "; pt2.showxy(); // 내부블럭끝 cout << "pt1 : "; pt1.showxy(); return 0; 블럭내에서객체 pt2 는블록이끝날때소멸자를호출하고, 객체 pt1 은주프로그램의시작에서선언되었으므로주프로그램이끝날때소멸자를호출한다.
소멸자예 (2) #include<iostream> using std::cout; using std::endl; class Person char *name; char *phone; int age; public: Person(char* _name, char* _phone, int _age); ~Person(); void ShowData(); Person::Person(char* _name, char* _phone, int _age) name=new char[strlen(_name)+1]; strcpy(name, _name); phone=new char[strlen(_phone)+1]; strcpy(phone, _phone); Person::~Person() delete []name; delete []phone; void Person::ShowData() cout<<"name: "<<name<<endl; cout<<"phone: "<<phone<<endl; cout<<"age: "<<age<<endl; int main() Person p("kim", "013-333-5555", 22); p.showdata(); 35 age=_age; return 0;
클래스와배열 Jong Hyuk Park
클래스와배열 객체배열과생성자 객체배열-객체를멤버로지니는배열 객체배열은기본적으로 void 생성자의호출을요구한다. PointArr1.cpp 37
객체배열예 (1) #include<iostream> using std::cout; using std::endl; class Point int x; int y; public: Point() cout<<"point() call!"<<endl; x=y=0; Point(int _x, int _y) x=_x; y=_y; int main() Point arr[5]; for(int i=0; i<5; i++) arr[i].setx(i*2); arr[i].sety(i*3); for(int j=0; j<5; j++) cout<<"x: "<<arr[j].getx()<<' '; cout<<"y: "<<arr[j].gety()<<endl; int GetX() return x; int GetY() return y; return 0; void SetX(int _x) x=_x; void SetY(int _y) y=_y; 38
클래스와배열 객체포인터배열 객체를가리킬수있는포인터를멤버로지니는배열 객체의동적생성방법 (PointArr2.cpp) Point* arr[5]; 스택 힙 39
객체배열예 (2) #include<iostream> using std::cout; using std::endl; class Point int x; int y; public: Point() cout<<"point() call!"<<endl; x=y=0; Point(int _x, int _y) x=_x; y=_y; int GetX() return x; int GetY() return y; void SetX(int _x) x=_x; void SetY(int _y) y=_y; int main() Point* arr[5]; for(int j=0; j<5; j++) cout<<"x: "<<arr[j]->getx()<<' '; cout<<"y: "<<arr[j]->gety()<<endl; for(int k=0; k<5; k++) delete arr[k]; // 힙에저장된객체소멸. 40 for(int i=0; i<5; i++) arr[i]=new Point(i*2, i*3); // new 에의한객체동적생성. return 0;
자기참조 (this 포인터 ) Jong Hyuk Park
자기참조 (self-reference) 자기참조 클래스의멤버함수는자신을호출한객체를가리키는포인터를명시 this 라는키워드가자기자신객체의포인터를가리킴 42
#include <iostream> using std::cout; using std::endl; 자기참조예 (1) class Person public: Person* GetThis() return this; //this 포인터를리턴. int main() cout<<"***** p1 의정보 ******"<<endl; Person *p1 = new Person(); cout<<" 포인터 p1: "<<p1<<endl; cout<<"p1 의 this: "<<p1->getthis()<<endl; cout<<"***** p2 의정보 ******"<<endl; Person *p2 = new Person(); cout<<" 포인터 p2: "<<p2<<endl; cout<<"p2 의 this: "<<p2->getthis()<<endl; 43 return 0;
자기참조 (self-reference) this 포인터의의미 this_under.cpp 가말해준다! p1 0x10 p2 0x20 44
#include <iostream> using std::cout; using std::endl; class Data int aaa; int bbb; public : 자기참조예 (2) Data(int aaa, int bbb) //aaa=aaa; this->aaa=aaa; //bbb=bbb; this->bbb=bbb; void printall() cout<<aaa<<" "<<bbb<<endl; 45 int main(void) Data d(100, 200); d.printall(); return 0;
자기참조예 (3) #include <iostream> using std::cout; using std::endl; class Dog Dog *body; char *name; Dog *tail; public: Dog(char *s); char *dog_name(); void make_tail(char *s); char *tail_name(); Dog::Dog(char *s) name = new char[strlen(s) + 1]; strcpy(name, s); tail = body = 0; char *Dog::dog_name() return name; void Dog::make_tail(char *s) tail = new Dog(s); // tail 객체생성 tail->body = this; // 자기참조포인터지정 char *Dog::tail_name() return tail->dog_name(); int main() Dog dog("happy"); dog.make_tail("merry"); cout << "dog name : " << dog.dog_name() << endl; cout << "tail name : " << dog.tail_name() << endl; return 0; 46
자기참조예 (3) dog name : Happy tail name : Merry 전용부분에서같은클래스의포인터로서 body, tail을선언하였다. make_tail() 함수에서새로생성한객체 tail의 body가호출한객체를가리키도록자기참조포인터 this를지정하였다. 객체 dog body name tail Happy body name tail Merry 47
friend 선언 Jong Hyuk Park
프렌드함수 (friend function) (1) C++ 언어의클래스의전용부분은같은객체의멤버함수만이접근가능하다는점에서객체지향프로그래밍에서의자료의은폐와캡슐화를지원 전용부분자료의접근을클래스의내부에국한시킴으로클래스내부의수정이클래스외부영역에전혀영향을주지못하기때문에프로그램의유지보수가용이 불가피하게클래스의멤버함수가아닌일반함수또는다른클래스의함수가클래스의전용부분에있는자료를사용해야만하는경우프렌드함수사용 프렌드함수는클래스의멤버함수가아닌다른멤버의함수가클래스의전용부분의자료에접근하는수단으로이용 49
프렌드함수 (2) 접근하는클래스에 friend 키워드로프렌드함수선언 프렌드함수의선언은클래스의전용부분이나공용부분어느곳에위치하든지차이가없음 전역함수에게 private 영역접근허용 class Ex friend int func(); // 클래스 Ex 의프렌드함수 func() 선언 50
friend 함수예 friend1.cpp #include <iostream> using std::cout; using std::endl; class Counter private: int val; public: Counter() val=0; void Print() const cout<<val<<endl; int main() Counter cnt; cnt.print(); 51 friend void SetX(Counter& c, int val); //friend 선언. void SetX(Counter& c, int val) // 전역함수. c.val=val; SetX(cnt, 2002); cnt.print(); return 0;
friend 클래스예 클래스가클래스를 friend 로선언 friend2.cpp 52 #include <iostream> using std::cout; using std::endl; class AAA private: int data; friend class BBB; // class BBB 를 friend 로선언함! class BBB public: void SetData(AAA& aaa, int val) aaa.data=val; //class AAA 의 private 영역접근! int main() AAA aaa; BBB bbb; bbb.setdata(aaa, 10); return 0;
friend 선언 friend 선언의유용성 유용하지않다! 정보은닉에위배되는개념 연산자오버로딩에서유용하게사용 그전에는사용하지말자! friend 선언으로만해결가능한문제 그런것은존재하지않는다. 연산자오버로딩에서는예외! 53
과제및실습 HW #1. 연습문제 4-2: Time 클래스프로그램 ** 모든연습문제풀어보기 ** 실습. 4-8절의은행계좌관리프로그램에다음을추가하여라 고객의전화번호정보를추가하여라. 전화번호는다음과같이 private으로선언하라. char *phone; ( 이름, 전화번호 ) 와계좌번호로잔액조회를하는 Inquire() 오버로딩함수를작성하여라. void Inquire(char *name, char *phone); void Inquire(int id); 54
Jong Hyuk Park