Development of Fashion CAD Systemfashiontech.snu.ac.kr/note/fashioncad/07-Object Oriented...

Post on 15-Oct-2019

5 views 0 download

Transcript of Development of Fashion CAD Systemfashiontech.snu.ac.kr/note/fashioncad/07-Object Oriented...

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;}