Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Sungmin Kim
SEOUL NATIONAL UNIVERSITY
Development of Fashion CAD System
7. Object Oriented Programming-1
Introduction Topics 기존 프로그래밍 기법의 문제점
낮은 코드 가독성 및 재사용성
– 사용자 인터페이스와 데이터의 무질서한 혼재
– 구조체 변수의 한계
Object Oriented Programming (OOP)
Class를 이용한 코드 가독성 및 재사용성 향상
Class의 기본적 정의
– 생성자 (constructor) / 복제생성자 (copy constructor) / 파괴자 (destructor)
– 멤버 변수 / 멤버 함수
– 추상화 (encapsulation)
– 다형성 (polymorphism)
Class관련 심화 주제
– 상속 (inheritance)
– 접근지정자 (Access Specifier)
– 연산자 오버로딩 (operator overloading)
2
Introduction 기존 프로그래밍 기법의 문제점 낮은 코드 가독성
사용자 인터페이스와 데이터가 혼재
– TForm 과 같이 사용자 인터페이스를 만드는 코드와 데이터 정의가 같이 됨
– Linux, Android, iOS 등 다른 운영체제, 다른 프레임워크를 쓰는 프로그램에 적용이 어려움
코드의 중복 및 복잡함
– 단순한 작업을 하기 위해서도 동일한 코드가 계속 반복됨
– 동적으로 할당한 구조체 내에 또다시 동적인 메모리를 할당하거나 할 때 복잡도가 기하급수적으로 증가
디버깅의 어려움
– 데이터가 체계적이지 않아서 오류가 발생했을 때 찾기가 어렵다
낮은 코드 재 사용성
유사한 기능의 다른 프로그램을 만들 때 코드를 재사용할 수 없다
표준 C 라이브러리와의 비교
– 표준 C 함수 처럼 어떤 프로그램에서도 사용이 가능한 형태의 코딩이 필요함
3
Introduction Struct 의 한계 극복 Struct 의 한계
연관된 여러가지 데이터를 하나로 묶어 대표 이름으로 사용
데이터끼리 연산이 필요한 경우 함수를 만들어야 함
데이터의 종류와 수가 많아지고 데이터를 조작하는 함수가 많아질수록 코드의 양이 증가
다른 프로그램에서 struct 를 쓰려면 관련된 코드가 전부 따라다녀야 해서 수정이 불가능
Class 개념의 등장
데이터를 조작하는데 필요한 함수들까지 전부 포함된 구조적 데이터 객체
클래스 내부의 복잡한 계산을 메인 프로그램과 완벽하게 분리 (Encapsulation)
– C 의 표준 함수와 같이 동작 가능
– 독자적 유지보수가 용이해짐
4
Introduction 객체 지향 프로그래밍 Class and Object
5
Class - Car
Methodrefuel(), getFuel(),
setSpeed(), getSpeed(),drive()
Propertycolor, size,
shape,speed, fuel
Object
Introduction 객체 지향 프로그래밍 잘 설계된 Class 의 예
Windows UI
– TControl Class의 여러 버젼
6
TCaption
TEdit
TShape
TComboBox
TButton
Introduction Class 설계의 이점 Modular Programming
주요 알고리즘만 개발하고 나머지는 기존 라이브러리(library)를 활용
– CAD 프로그램 개발에 필요한 기하학적 요소 처리 클래스
– 이미지 프로세싱 개발에 필요한 대용량 이미지 핸들링 클래스
– 수치해석 시스템 개발에 필요한 미적분 해석 엔진 클래스
– 하드웨어 컨트롤 시스템 개발에 필요한 I/O 컨트롤 클래스
7
Point Class Struct 와 Class 의 비교 ptPoint 구조체를 class 로 재정의 할 경우
struct 키워드가 class 로 바뀐다
public: 이라는 access specifier 가 추가된다
ptPoint(), ptPoint(ptPoint&) 와 ~ptPoint() 라는 special member function 들이 추가된다
가급적 하나의 클래스는 하나의 unit으로 저장한다
– 클래스 정의는 ptPoint.h 헤더 파일로 (이 클래스를 사용하려고 할 때 include 해주어야 함)
– 클래스 구현에 필요한 소스는 ptPoint.cpp 파일로
8
struct ptPoint{
float x, y;};
class ptPoint{public:
ptPoint();ptPoint(ptPoint&);~ptPoint();
float x, y;};
Point Class Constructor & Destructor Constructor
Class 정의를 써서 object 를 만들 때 처음으로 실행되는 함수
– 멤버 변수들의 초기값을 결정할 수 있다
– Struct 를 만들 때는 일일이 값을 초기화 해주어야 한다
– 멤버 변수가 많은 경우 프로그래머가 일일이 지정해주지 않아도 된다
Copy Constructor
이미 존재하는 object 를 복사해서 새로운 object 를 만들 때 사용되는 함수
– 심층 복사 (deep level copy) 를 간단하게 수행할 수 있음
– ptPoint a,b 가 있을 때 a=b 와 같은 조작이 가능
Destructor
생성된 object 를 없앨 때 실행되는 함수
– 동적 메모리로 할당된 멤버변수들을 해제
» delete, free 함수 등
– 기타 object 가 없어질 때 해야하는 많은 procedure 를 프로그래머가 지정하지 않아도 수행하게 됨
9
Point Class Constructor & Destructor ptPoint struct 의 변환
FIle-New-Unit 으로 새로운 unit 을 프로젝트에 추가한 뒤 ptPoint.cpp 로 저장
10
//---------------------------------------------------------------#pragma hdrstop#include "ptPoint.h"//---------------------------------------------------------------#pragma package(smart_init)
ptPoint::ptPoint(){x=y=0;}
ptPoint::ptPoint(ptPoint &p){x=p.x;y=p.y;}
ptPoint::~ptPoint(){}
//----------------------------------------------------------#ifndef ptPointH#define ptPointH//----------------------------------------------------------
class ptPoint{public :
ptPoint();ptPoint(ptPoint&);~ptPoint();
float x,y;};
#endif
ptPoint.h ptPoint.cpp
Point Class Object의 생성 Pointer 사용
C++ 에서의 Class 객체는 new keyword 를 써서 생성한다
– ptPoint *P=new ptPoint()
이 때 하나의 객체를 생성하기 위해서는 하나의 포인터가 필요하다
따라서, 동적으로 여러 개의 객체를 생성하려면 '포인터의 배열' 이 필요하다
– ptPoint **P;
TChildForm.h 의 ptPoint 배열 정의를 다음과 같이 변경
11
int PointNum;ptPoint **Point;
Point Class Object의 생성 AddPoint 함수의 수정
생성자를 하나 더 정의할 수 있다 : Polymorphism (다형성, 多形性)
12
void __fastcall TChildForm::AddPoint(float x,float y){Point=(ptPoint**)realloc(Point,sizeof(ptPoint*)*(PointNum+1));Point[PointNum]=new ptPoint;Point[PointNum]->x=x;Point[PointNum]->y=y;PointNum++;}
class ptPoint{public :
ptPoint();ptPoint(float,float);ptPoint(ptPoint&);~ptPoint();
float x,y;};
ptPoint::ptPoint(float X,float Y){x=X;y=Y;}
void __fastcall TChildForm::AddPoint(float x,float y){Point=(ptPoint**)realloc(Point,sizeof(ptPoint*)*(PointNum+1));Point[PointNum++]=new ptPoint(x,y);}
코드의 단순화
Point Class Object 내의 요소의 참조 포인터의 배열로 되어 있는 객체 내의 요소를 참조 (refer)
Struct의 경우
– ptPoint *pt 로 배열을 만든 경우
– pt[0].x 와 같이 '.' 연산자로 struct 내의 요소를 참조할 수 있다
Class의 경우
– ptPoint **pt 로 포인터 배열을 만든 경우
– pt[0]->x 와 같이 '->' 연산자로 포인터 pt[0] 이 가리키는 객체 (object) 내의 요소를 참조할 수 있다
연산자 변경
– 소스 내의 ptPoint 구조체와 관련된 모든 '.' 연산자를 '->' 로 바꿔야 한다
– 매우 단순한 객체는 여전히 ptPoint a 와 같이 정의할 수도 있음
– 내부적으로 복잡한 객체의 경우 문제가 발생할 수 있음
» 반드시 ClassName *object=new ClassName 과 같이 정의하는 습관이 필요함
13
Point Class Member Function 의 정의 새로운 문제 1
ptPoint 와 관련된 모든 '.' 연산자를 '->' 연산자로 수정한 뒤에 남아있는 문제
DistanceToSegment 함수의 인자는 ptPoint Object여야 한다
– 현재의 코드는 object 의 pointer 를 넘겨주는 방식이므로 에러가 발생
14
float __fastcall TChildForm::DistanceToLine(int l,int x,int y){if (Line[l].Type==0){ // line
return DistanceToSegment(x,y,Point[Line[l].Point[0]],Point[Line[l].Point[1]]);}
else{ // Bezier curveint i;float d,MinD=10000;for(i=0;i<Line[l].PointNum-1;i++){
d=DistanceToSegment(x,y,Point[Line[l].Point[i]],Point[Line[l].Point[i+1]]);if (d<MinD){
MinD=d;}
}return MinD;}
}
Point Class Member Function 의 정의 새로운 문제 1
Pass-by-Reference 방식으로 object 의 pointer 를 넘겨주면 해결
15
float __fastcall DistanceToSegment(int x,int y, ptPoint *a,ptPoint *b);
float __fastcall TChildForm::DistanceToSegment(int x,int y,ptPoint *A,ptPoint *B){float MinD,d,m;ptPoint a,b;a=Screen(A->x,A->y); // 원본이 변화될 수 있으므로 기존 소스를 수정해야 함b=Screen(B->x,B->y);MinD=sqrt((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y));d=sqrt((b.x-x)*(b.x-x)+(b.y-y)*(b.y-y));if (d<MinD) MinD=d;if (a.x==b.x){
d=fabs(x-a.x);}
else{m=(a.y-b.y)/(a.x-b.x);d=fabs(m*x-y+a.y-m*a.x)/sqrt(m*m+1);}
if (d<MinD) MinD=d;return MinD;}
Point Class Member Function 의 정의 새로운 문제 2
MirrorPoint 함수의 인자도 object 여야 함
16
void __fastcall TChildForm::AddMirrorPoints1Click(TObject *Sender){
if (SelNum<2) return;if (Sel[0].Type!=1 || Line[Sel[0].Num].Type!=0){
MessageBox(Handle,"Select a line first","Caution",MB_ICONEXCLAMATION);}
int i;
ptPoint a=Point[Line[Sel[0].Num].Point[0]];ptPoint b=Point[Line[Sel[0].Num].Point[1]];
for(i=1;i<SelNum;i++){ptPoint p=MirrorPoint(a,b,Point[Sel[i].Num]);AddPoint(p.x,p.y);}
FormPaint(this);}
Point Class Member Function 의 정의 새로운 문제 2
MirrorPoint 함수의 인자도 object 여야 함
– 먼저 a, b 를 포인터로 변경
17
void __fastcall TChildForm::AddMirrorPoints1Click(TObject *Sender){
if (SelNum<2) return;if (Sel[0].Type!=1 || Line[Sel[0].Num].Type!=0){
MessageBox(Handle,"Select a line first","Caution",MB_ICONEXCLAMATION);}
int i;
ptPoint *a=Point[Line[Sel[0].Num].Point[0]];ptPoint *b=Point[Line[Sel[0].Num].Point[1]];
for(i=1;i<SelNum;i++){ptPoint p=MirrorPoint(a,b,Point[Sel[i].Num]);AddPoint(p.x,p.y);}
FormPaint(this);}
Point Class Member Function 의 정의 새로운 문제 2
포인터 형으로 인자를 변경
a, b, c 와 관련된 연산자 수정
– '.' 를 '->' 로 수정
18
ptPoint __fastcall TChildForm::MirrorPoint(ptPoint *a,ptPoint *b,ptPoint *c){float m,n,p,q;ptPoint d,r;
if (a->x==b->x){d.x=2*a->x-c->x;d.y=c->y;}
else{if (a->y==b->y){
d.x=c->x;d.y=2*b->y-c->y;}
else{m=(a->y-b->y)/(a->x-b->x);n=a->y-m*a->x;p=-1/m;q=c->y-p*c->x;r.x=(n-q)/(p-m);r.y=m*r.x+n;d.x=2*r.x-c->x;d.y=2*r.y-c->y;}
}return d;}
Point Class Member Function 의 정의 소극적인 해결 방법
Pointer 참조로 해결된 듯 보이지만 여전히 코드가 너무 복잡하다
적극적인 해결 방법
두 점 사이의 거리 라든가, 직선에 대한 대칭점의 위치 등은 '점' 과 밀접한 관계가 있다
ptPoint class의 member 함수로 만드는 것이 최상의 방법
– ptPoint class 에 다음과 같이 멤버 함수를 정의한다
19
class ptPoint{public :
...
float x,y;
float DistanceToSegment(ptPoint,ptPoint);ptPoint MirrorPoint(ptPoint,ptPoint);
};
Point Class Member Function 의 정의 DistanceToSegment Member Function 작성
TChildForm 에 있는 함수를 ptPoint.cpp 로 옮기기
– Screen 좌표로 변환된 a, b 를 전달
– TChildForm 의 DistanceToSegment 는 삭제
20
float ptPoint::DistanceToSegment(ptPoint a,ptPoint b){float MinD,d,m;MinD=sqrt((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y));d=sqrt((b.x-x)*(b.x-x)+(b.y-y)*(b.y-y));if (d<MinD) MinD=d;if (a.x==b.x){
d=fabs(x-a.x);}
else{m=(a.y-b.y)/(a.x-b.x);d=fabs(m*x-y+a.y-m*a.x)/sqrt(m*m+1);}
if (d<MinD) MinD=d;return MinD;}
Point Class Member Function 의 정의 DistanceToSegment Member Function 작성
TChildForm 의 함수를 수정
21
float __fastcall TChildForm::DistanceToLine(int l,int x,int y){ptPoint P,A,B;P.x=x;P.y=y;if (Line[l].Type==0){ // line
A=Screen(Point[Line[l].Point[0]]->x,Point[Line[l].Point[0]]->y);B=Screen(Point[Line[l].Point[1]]->x,Point[Line[l].Point[1]]->y);return P.DistanceToSegment(A,B);}
else{ // Bezier curveint i;float d,MinD=10000;for(i=0;i<Line[l].PointNum-1;i++){
A=Screen(Point[Line[l].Point[i]]->x,Point[Line[l].Point[i]]->y);B=Screen(Point[Line[l].Point[i+1]]->x,Point[Line[l].Point[i+1]]->y);d=P.DistanceToSegment(A,B);if (d<MinD){
MinD=d;}
}return MinD;}
}
Point Class Member Function 의 정의 MirrorPoint Member Function 작성
TChildForm 의 함수를 ptPoint로 이동
– ptPoint object 를 인수로 전달
– return 값도 ptPoint object
22
ptPoint ptPoint::MirrorPoint(ptPoint a,ptPoint b){float m,n,p,q;ptPoint d,r;if (a.x==b.x){
d.x=2*a.x-x;d.y=y;}
else{if (a.y==b.y){
d.x=x;d.y=2*b.y-y;}
else{m=(a.y-b.y)/(a.x-b.x);n=a.y-m*a.x;p=-1/m;q=y-p*x;r.x=(n-q)/(p-m);r.y=m*r.x+n;d.x=2*r.x-x;d.y=2*r.y-y;}
}return d;}
Point Class Member Function 의 정의 MirrorPoint Member Function 작성
TChildForm 코드의 수정
23
void __fastcall TChildForm::AddMirrorPoints1Click(TObject *Sender){if (SelNum<2) return;if (Sel[0].Type!=1 || Line[Sel[0].Num].Type!=0){
MessageBox(Handle,"Select a line first","Caution",MB_ICONEXCLAMATION);}
int i;
ptPoint a=*Point[Line[Sel[0].Num].Point[0]];ptPoint b=*Point[Line[Sel[0].Num].Point[1]];
for(i=1;i<SelNum;i++){ptPoint p=Point[Sel[i].Num]->MirrorPoint(a,b);AddPoint(p.x,p.y);}
FormPaint(this);}
Point Class Destructor 부르기 TChildForm 의 Close event 코드의 수정
24
void __fastcall TChildForm::FormClose(TObject *Sender, TCloseAction &Action){if (Point){
int i;for(i=0;i<PointNum;i++) delete Point[i];free(Point);}
Point=0; PointNum=0;if (Line) free(Line);Line=0;if (Pattern) free(Pattern);Pattern=0;if (Sel) free(Sel);Sel=0;Action=caFree;}
Point Class 중복된 코드의 검사 FindObject 함수
sqrt 함수로 두 점 간의 거리를 구하는 코드가 중복 출현
– 두 점 간의 거리를 구하는 함수를 ptPoint 의 member 함수로 만든다면 ?
25
// find point :
for(i=0;i<PointNum;i++){ptPoint P=NewCanvas->Screen(*Point[i]);d=sqrt((P.x-x)*(P.x-x)+(P.y-y)*(P.y-y));if (d<MinD){
MinD=d;MinO=i;ObjectType=0;}
}
// find pattern :
MinD=10;for(i=0;i<PatternNum;i++){
ptPoint P=NewCanvas->Screen(Pattern[i].Center);d=sqrt((P.x-x)*(P.x-x)+(P.y-y)*(P.y-y));if (d<MinD){
MinD=d;MinO=i;ObjectType=2;}
}return MinO;
Point Class 중복된 코드의 검사 Distance 함수의 추가
ptPoint 클래스에 추가
– Polymorphism
26
float Distance(float,float);float Distance(ptPoint);
float ptPoint::Distance(float X,float Y){return sqrt((x-X)*(x-X)+(y-Y)*(y-Y));}
float ptPoint::Distance(ptPoint p){return Distance(p.x,p.y);}
Point Class 중복된 코드의 검사 TChildForm 코드 수정
FindObject 함수
중복되는 코드나, 연관있는 코드는 해당 클래스의 member function 으로 만드는 것이 최선
27
// find point :
ptPoint OP;OP.x=x;OP.y=y;for(i=0;i<PointNum;i++){
ptPoint P=NewCanvas->Screen(*Point[i]);d=OP.Distance(P);if (d<MinD){
MinD=d;MinO=i;ObjectType=0;}
}if (MinD<5) return MinO;
// find pattern :
MinD=10;for(i=0;i<PatternNum;i++){
ptPoint P=NewCanvas->Screen(Pattern[i].Center);d=OP.Distance(P);if (d<MinD){
MinD=d;MinO=i;ObjectType=2;}
}return MinO;
New Canvas Class New Canvas Class 좌표 변환 기능이 더해진 Canvas Class
실제좌표-화면좌표간의 변환을 수행
ptPoint 클래스와 함께 재사용 가능
ptNewCanvas 클래스를 작성
– ptNewCanvas.cpp 유닛
28
//---------------------------------------------------------------#ifndef ptNewCanvasH#define ptNewCanvasH//---------------------------------------------------------------
#include "ptPoint.h"
class ptNewCanvas{public:
ptNewCanvas();~ptNewCanvas();
ptPoint SC,O;float R;
void FitToWindow(float,float,float,float,float,float);ptPoint Screen(float,float);ptPoint Screen(ptPoint);ptPoint Real(float,float);ptPoint Real(ptPoint);
};
#endif
New Canvas Class New Canvas Class ptNewCanvas 의 멤버 함수 작성
좌표 변환과 관련된 변수, 함수를 ptNewCanvas 클래스로 이전
29
void ptNewCanvas::FitToWindow(float width,float height,float x1,float y1,float x2,float y2){O.x=(x1+x2)/2;O.y=(y1+y2)/2;float W=width;float H=height;SC.x=W/2;SC.y=H/2;float w=x2-x1;float h=y1-y2;if (W>H){
R=W/w;if (R*h>H) R=H/h;}
else{R=H/h;if (R*w>W) R=W/w;}
}
New Canvas Class New Canvas Class ptNewCanvas 의 멤버 함수 작성
좌표 변환과 관련된 변수, 함수를 ptNewCanvas 클래스로 이전
– Polymorphism 을 이용한 여러 버젼의 함수 선언 가능
30
ptPoint ptNewCanvas::Screen(ptPoint p){return Screen(p.x,p.y);}
ptPoint ptNewCanvas::Screen(float rx,float ry){ptPoint P;P.x=SC.x+(rx-O.x)*R;P.y=SC.y-(ry-O.y)*R;return P;}
ptPoint ptNewCanvas::Real(ptPoint p){return Real(p.x,p.y);}
ptPoint ptNewCanvas::Real(float sx,float sy){ptPoint P;P.x=(sx-SC.x)/R+O.x;P.y=O.y-(sy-SC.y)/R;return P;}
New Canvas Class New Canvas Class TChildForm 쪽 소스 수정
ptNewCanvas 클래스를 만들고 멤버 함수를 부르도록 수정
– 생성자에서 NewCanvas=new ptNewCanvas; 로 object 생성
– Close 이벤트에서 delete NewCanvas; 로 object 파괴
– Screen, Real 함수는 전부 NewCanvas->Screen, NewCanvas->Real 함수로 대체
31
void __fastcall TChildForm::FormResize(TObject *Sender){NewCanvas->FitToWindow(ClientWidth,ClientHeight,-500,500,500,-500);FormPaint(this);}
Screen(Point[i].x,Point[i].y) NewCanvas->Screen(*Point[i]);
New Canvas Class New Canvas Class 새로운 문제
Mouse 핸들러 쪽에서 문제가 발생
– Mouse 핸들링도 NewCanvas 의 member function 으로 만드는 것이 좋다
– 변수의 초기화
32
#include <vcl.h> // TMouseButton, TShiftState 등이 정의된 헤더 파일....
class ptNewCanvas{...
bool DragStart,Zooming;ptPoint Start;void MouseDown(TMouseButton Button,TShiftState Shift, int X, int Y);void MouseMove(TShiftState Shift,int X, int Y);void MouseUp(TMouseButton Button,TShiftState Shift, int X, int Y);...
ptNewCanvas::ptNewCanvas(){DragStart=false;}
New Canvas Class New Canvas Class TChildForm 쪽 소스 수정
마우스 핸들러 중에서 화면과 관련된 것은 NewCanvas 로 이전
33
void __fastcall TChildForm::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y){if (Shift.Contains(ssCtrl)){
int o=FindObject(X,Y);if (!Shift.Contains(ssShift)){
SelNum=0;if (Sel) free(Sel);Sel=0;}
if (o!=-1){AddSelection(ObjectType,o);}
FormPaint(this);}
else{NewCanvas->MouseDown(Button,Shift,X,Y);}
}
void __fastcall TChildForm::FormMouseMove(TObject*Sender,TShiftState Shift, int X, int Y){if (NewCanvas->MouseMove(Shift,X,Y)) FormPaint(this);}
void __fastcall TChildForm::FormMouseUp(TObject*Sender,TMouseButton Button, TShiftState Shift, int X, int Y){NewCanvas->MouseUp(Button,Shift,X,Y);}
New Canvas Class New Canvas Class NewCanvas member function 작성
34
void ptNewCanvas::MouseDown(TMouseButtonButton,TShiftState Shift, int X, int Y){if (!DragStart){
DragStart=true;if (Button==mbRight) Zooming=true;else Zooming=false;Start.x=(float)X;Start.y=(float)Y;}
}
bool ptNewCanvas::MouseMove(TShiftState Shift,int X, int Y){if (DragStart){
float dx=(float)X-Start.x;float dy=(float)Y-Start.y;if (Zooming==true){
if (dy<0) R*=1.01;else R*=0.99;}
else{O.x-=dx/R;O.y+=dy/R;}
Start.x=(float)X;Start.y=(float)Y;return true;}
return false;}
void ptNewCanvas::MouseUp(TMouseButton Button,TShiftState Shift, int X, int Y){if (DragStart) DragStart=false;}