책정리/혼자 연구하는 C,C++ 2

25장 클래스

GONII 2015. 2. 20. 12:11

25.1 OOP

25.1.1 소프트웨어 위기

하드웨어의 발전에 힘입어 컴퓨터 가격이 저렴해지고 활발하게 보급됨에 따라 컴퓨터의 활용 분야가 점점 넓어졌다. 컴퓨터가 업무와 생활 곳곳에 활용됨에 따라 특수한 용도에 맞는 다양한 소프트웨어가 필요해졌다.

또한 하드웨어가 빨라지고 대용량화됨으로써 컴퓨터로 할 수 있는 일들이 많아져 소프트웨어의 기능도 과거보다 훨씬 더 복잡해지고 만들기도 어려워졌다.

그러나 소프트웨어가 하드웨어의 발전을 따라가지 못하는 이런 현상을 소프트웨어 위기(Software Crisis)라고 함

   

소프트웨어의 위기 원인

  1. 기존 절차식 프로그래밍 방법의 낮은 생산성

    절차식 프로그래밍은 간결하고 빠른 실행 파일을 만들기는 하지만 규모가 커지면 개발뿐만 아니라 유지보수에 한계를 드러 냈다.코드의 일반성이 없으므로 한 번 만든 코드가 수정없이 재사용되는 경우가 드물며 매번 현실의 문제에 맞게 처음부터 다시 개발해야 한다.

  2. 대규모의 소프트웨어를 만드는데 수년의 개발 기간과 과다한 인력을 소모하게 되었다.

    고급인력의 부족은 소프트웨어의 공급 부족을 초래했고 시간의 부족은 소프트웨어의 질을 저하시켰다.

    하드웨어는 이미 만들어져 있는 트랜지스터나 IC 등을 조립하는 방식으로 개발할 수 있으며 부품의 신뢰성이 높으므로 완제품의 질도 좋을 수밖에 없다. 이에 비해 소프트웨어는 항상 처음부터 다시 개발해야 하므로 시간이 걸리고 신뢰성이 떨어진다.

       

    절차지향의 대안 객체 지향적 프로그래밍(Object Oriendted Programming)

    문제의 핵심인 데이터를 정의하고 데이터에 절차를 결합하여 현실의 사물을 표현할 수 있는 객체를 만든다. 그리고 이러한 독립적인 객체를 조립하여 프로그램을 완성해 나가는 방식이다.

25.1.2 OOP의 특징

  • 캡슐화 (Encaapsulation)

    표현하고자 하는 자료(Data)와 동작(Function)을 하나의 단위로 묶는 것이며 이렇게 묶어 놓은 것을 객체(Object)라고 한다. 대상의 특징을 나타내는 데이터와 이 데이터를 관리하는 함수가 항상 하나의 묶음으로 사용되므로 객체는 스스로 독립적이며 프로그램의 부품으로 활용될 수 있다. 그래서 객체를 소프트웨어 IC라고 부르기도 한다.

  • 정보 은폐 (Information Hiding)

    객체는 자신의 상태를 기억하기 위한 속성과 속성을 관리하는 동작을 정의한다. 이중 외부에서 사용하는 기능만 공개하고 나머지는 숨길 수 있는데 이를 정보 은폐라고 한다. 외부에서 객체의 상태를 마음대로 바꾸거나 허가되지 않은 동작을 요청하지 못하도록 함으로써 스스로의 안전성을 확보하는 수단이며 정보 은폐에 의해 객체는 더욱 견고하게 캡슐화된다.

  • 추상화 (Abstraction)

    현실의 사물을 객체로 표현하기 위해서는 이 사물이 어떤 특징을 가지며 어떤 동작이 가능한지를 조사해야 하는데 이를 데이터 모델링이라고 한다. 모델링의 결과 필요한 자료와 동작의 목록이 작성되며 이들을 캡슐화하여 객체로 정의한다. 추상화란 객체의 효율적이고도 안전한 사용을 위해 인터페이스를 설계하는 것이며 캡슐화와 정보 은폐에 의해 구현된다. 개념화라고도 한다.

  • 상속 (Inheritance)

    상속은 이미 만들어진 클래스를 파생시켜 새로운 클래스를 정의하는 기법이다. 파생된 클래스는 기존 클래스의 모든 속성과 동작을 물려받으며 여기에 더 필요한 기능을 추가하거나 필요없는 기능을 제거 또는 변경할 수 있다.

  • 다형성 (Polymorphism)

    똑같은 호출이라도 상황에 따라, 호출하는 객체에 따라 다른 동작을 할 수 있는 능력을 다형성이라고 한다. 실제 내부 구현은 다르더라도 개념적으로 동일한 동작을 하는 함수를 하나의 인터페이스로 호출할 수 있으므로 객체들을 사용하는 코드를 일관되게 유지할 수 있다. 다형성은 동적 바인딩을 하는 가상 함수에 의해 구현된다.

특징

간단한 설명

캡슐화

묶는다

정보 은폐

숨긴다

추상화

표현한다

상속

재사용한다

다형성

상황에 따라 달라진다

25.1.3 OOP 맛보기

절차 지향 프로그램 : 하향식(Top Down)

객체 지향 프로그램 : 상향식(Bottom up)

  • 예제 RandNumOop

#include <iostream>

#include <time.h>

   

using namespace std ;

   

class randNum

{

private :

int                num ;

   

public :

randNum( void ) ;

void        generate ( void ) ;

bool        compare ( int input ) ;

} ;

   

class ask

{

private :

int                input ;

   

public :

void        prompt( void ) ;

bool        askUser( void ) ;

int                getInput( void ) ;

} ;

   

void main ( void )

{

randNum R ;

ask A ;

   

for ( ; ; )

{

R.generate( ) ;

A.prompt( ) ;

for ( ; ; )

{

if (A.askUser( ))

{

exit( 0 ) ;

}

if ( R.compare( A.getInput( ) ))

{

break ;

}

}

}

}

   

randNum::randNum( void )

{

srand( (unsigned)time(NULL) ) ;

}

   

void        randNum::generate ( void )

{

num = rand()%100 ;

}

   

bool        randNum::compare ( int input )

{

if ( input == num )

{

cout << "맞췄습니다." << endl ;

return true ;

}

else if ( input > num )

{

cout << "입력한 숫자보다 더 작습니다." << endl ;

}

else

{

cout << "입력한 숫자보다 더 큽니다." << endl ;

}

return false ;

}

   

void        ask::prompt()

{

cout << endl << "제가 만든 숫자를 맞춰보세요." << endl ;

}

   

bool        ask::askUser( void )

{

cout << "숫자를 입력하세요(긑낼 대는 999) : " ;

cin >> input ;

if ( input == 999 )

{

return true ;

}

return false ;

}

   

int                ask::getInput()

{

return input ;

}

   

객체가 완성되면 main에서는 부품을 조립하여 객체들끼리 서로 연결되어 동작하도록 한다.

   

  1. 만들어진 객체는 재사용 가능하다.
  2. 부품의 안전성이 높아진다.
  3. 확장성이 더 좋아졌다.
  4. 개발 속도가 빨라진다.

객체 지향 프로그래밍을 좀 쉽게 표현하자면 부품 조립식 개발 방법이며 부품이 되는 객체를 얼마나 잘 만드는가가 관건이다. 재사용할 수 있어야 하고 어떤 상황에서라도 제 기능을 발휘할 수 있는 범용성이 있어야 하며 부주의한 사용으로부터 자신을 지키는 안전성이 확보되어야 하고 확장 가능해야 한다.

   

C++은 객체 지향 언어가 아닌 혼합형 언어라고 한다.

비록 클래스를 쓰더라도 이 클래스가 캡슐화, 추상화를 제대로 하지 못한다면 부품으로서의 가치가 없고 재활용성이나 안전성이 떨어질 것이다. 클래스만 쓴다고 해서 무조건 객체지향이라고 볼 수 없다.

중요한 것은 항상 객체를 중심으로 문제를 분석하는 사고방식의 변화인데 이는 많은 연습과 모방을 필요로 하며 대로는 기존 습관에서 과감히 벗어날 필요도 있다.

25.2 C++로의 확장

25.2.1 개선 사항

  • C++에 새로 추가된 기능

내용

위치

간단한 설명

범위 연산자

7.3.1

지역변수에 의해 가려진 전역변수를 참조한다.

명시적 캐스팅

5.3.4

(int)var 형식이 아닌 int(var) 형식으로 캐스팅 한다.

인라인 함수

16.3

본체가 호출부에 삽입되는 함수

디폴트 인수

16.4

실인수가 생략될 때 형식 인수에 적용되는 기본값

함수 오버로딩

16.5

같은 이름의 함수를 여러 개 정의하는 기능

태그가 타입으로 승격됨

13.1.2

구조체 태그로부터 변수를 바로 선언할 수 있다.

이름 없는 공용체

13.5.2

공용체 이름없이 멤버들이 기억 장소를 공유한다.

한줄 주석

2.4.1

//로 줄 끝까지 주석을 단다.

레퍼런스

15.4

변수에 대한 별명을 붙인다.

bool 타입

3.7.5

1바이트의 진위형 타입

25.2.2 IOStream

  • 예제 cout

#include <iostream>

using namespace std ;

   

void main ( void )

{

cout << "welcome c++" << endl ;

}

cout << 데이터 << 데이터 ... ;

   

  • 예제 coutmulti

#include <iostream>

   

using namespace std ;

   

void main ( void )

{

int i = 123 ;

char ch = 'a' ;

double d = 3.14 ;

char str[] = "문자열" ;

   

cout << i << ch << d << str << endl ;

}

   

  • 예제 cin

#include <iostream>

   

using namespace std ;

   

void main ( void )

{

int i ;

cout << "정수를 입력하시오 : " ;

cin >> i ;

cout << "입력한 값은 " << i << "입니다." << endl ;

}

   

25.2.3 new

포인터 = new 타입[(초기값)] ;

new 다음에 할당 대상 타입을 밝히면 sizeof(타입) 만큼의 메모리가 할당되고 할당된 포인터가 리턴된다.

new가 리턴하는 번지는 같은 타입의 포인터 변수로 대입 받는다.

  • 예제 newdelte

#include <iostream>

   

using namespace std ;

   

void main ( void )

{

int *pi = new int ;

*pi = 123 ;

cout << "*pi = " << *pi << endl ;

   

delete pi ;

}

  1. malloc/free는 라이브러리가 제공하는 함수인데 비해 new/delete는 언어가 제공하는 연사자이다.

    그래서 별도의 헤더 파일을 포함할 필요없이 언제든지 사용할 수 있으며 이 연산자를 쓴다고해서 프로그램이 커지는 것도 아니다. 연산자 이기 때문에 사용자 정의 타입에 대해 오버로딩할 수 있다.

  2. 할당 타입과 같은 타입의 포인터 변수로 대입만 받으면 된다.

    malloc 함수는 필요한 메모리양을 바이트 단위로 지정하고 void *를 리턴하므로 sizeof 연산자와 캐스트 연산자의 도음을 받아야 한다. 이에 비해 new는 할당할 타입을 지정하고 해당 타입의 포인터를 리턴하므로 sizeof 연산자와 캐스트 연산자를 쓸 필요가 없다.

  3. 메모리 할당과 동시에 초기화 할 수 있다.

    malloc은 메모리를 할당하는 것만이 목적이므로 초기값을 줄 수 없지만, new 연산자는 동적으로 생성한 변수의 초기값을 지정할 수 있다. 즉 할당과 동시에 초기화를 할 수 있는데 할당 타입 다음의 괄호에 초기값을 적어 주면 된다. int *pi=new int ; *pi=123 ; 두 문장은 int *pi=new int(123) ; 하나로 합칠 수 있다.

  4. new 연산자로 객체를 할당할 때 생성자가 자동으로 호출된다.

    생성자란 객체를 자동으로 초기화하는 특별한 함수인데 다음 장에서 배우게 될 것이다. 생성자는 생성과 동시에 객체를 초기화할 수 있도록 함으로써 클래스가 기존 타입과 동등한 자격을 가지도록 하는 중요한 역할을 한다. 생성자를 호출한다는 점이 malloc과 new의 가장 큰 차이점이며 c++에서 별도의 할당 연산자가 추가된 이유이다. 마찬가지로 delete로 객체를 삭제할 때는 파괴자라는 특별한 함수가 자동으로 호출된다.

  • 예제 newstruct

#include <iostream>

using namespace std ;

   

struct tag_Friend

{

char name[10] ;

int age ;

double height ;

} ;

   

void main ( void )

{

tag_Friend *pF = new tag_Friend ;

   

strcpy( pF->name, "아무개" ) ;

pF->age = 22 ;

pF->height = 177.3 ;

   

cout << "이름 = " << pF->name << ", 나이 = " << pF->age << ", 키 = " << pF->height << endl ;

   

delete pF;

}

  

   

  • 예제 newarray

#include <iostream>

using namespace std ;

   

void main ( void )

{

int *ar = new int[5] ;

int i ;

   

for ( i = 0 ; i < 5 ; i++ )

{

ar[i] = i ;

}

   

for ( i = 0 ; i < 5 ; i++ )

{

cout << i << "번째 = " << ar[i] << endl ;

}

   

delete [] ar ;

}

new int[5]; 에 의해 정수형 변수 5개를 저장할 수 있는 메모리가 할당되며 int * 타입이 리턴된다.

리턴값을 int *형의 ar로 대입받으면 ar은 정수형 배열과 동등하며 배열처럼 사용할 수 있다.

   

int n ;

cout << "몇개 필요??" ;

cin >> i ;

int *a = new int[n] ;

   

변수로 사용 가능

25.3 구조체의 확장

25.3.1 멤버 함수

  • 예제 pos1

#include <iostream>

using namespace std ;

   

struct position

{

int x ;

int y ;

char ch ;

} ;

   

void outPosition (position pos)

{

cout << pos.ch << endl ;

}

   

void main ( void )

{

position here ;

here.x = 30 ;

here.y = 10 ;

here.ch = 'a' ;

outPosition (here) ;

   

}

작업은 outPosition 함수가 처리하는데 인수로 전달받은 구조체 pos 정보대로 지정한 위치에 문자를 출력했다.

이 예제에서 position 구조체와 outPosition 함수는 상호 의존적인 관계에 있다.

  

   

  • 예제 pos2

#include <iostream>

using namespace std ;

   

struct position

{

int x ;

int y ;

char ch ;

void outPosition()

{

cout << x << endl ;

cout << y << endl ;

cout << ch << endl ;

}

} ;

   

void main ( void )

{

position here ;

   

here.x = 30 ;

here.y = 40 ;

here.ch = 'a' ;

   

here.outPosition() ;

}

   

멤버 변수와 멤버 함수 모두 구조체 안에 들어갈 수 있다.

변수든 함수든 논리적으로 관련된 것을 한 곳에 모아 묶어 놓음으로서 구조체가 프로그램 부품 역할을 할 수 있게 된 것이다.

25.3.2 멤버 함수 작성법

  • 예제 pos3

#include <iostream>

using namespace std ;

   

struct position

{

int x ;

int y ;

char ch ;

void outPosition() ;

} ;

   

void position::outPosition()

{

cout << x << endl ;

cout << y << endl ;

cout << ch << endl ;

}

   

void main ( void )

{

position here ;

here.x = 30 ;

here.y = 40 ;

here.ch = 'a' ;

here.outPosition() ;

}

리턴타입 소속구조체::멤버함수(임수)

{

함수 본체

}

   

내부 정의 : 인라인 속성을 가진다.

실제로 호출되는 것이 아니라 멤버 함수를 호출하는 코드가 함수의 본체 코드로 대체된다.

   

외부정의 : 일반적인 함수 호출과 마찬가지 방법으로 멤버를 호출한다.

스택을 경유하여 인수를 넘기고 제어의 분기가 발생한다.

   

인라인 함수는 호출 부담이 없기 때문에 속도가 빠르지만 여러 번 호출할 경우 실행 파일의 크기를 증가시키는 단점이 있다.

25.3.3 액세스 지정

구조체 내부의 멤버를 외부에서 마음대로 건드리도록 내버려두면 부주의한 사용으로 인해 버그가 발생할 수 있어 위험할 뿐만 아니라 객체의 독립성도 떨어진다. 그래서 C++에서는 구조체(또는 클래스)의 멤버에 대한 외부의 참조를 허가할 것인지 금지할 것인지를 지정할 수 있다.

  • private

    이 속성을 가지는 멤버의 외부에서 액세스 할 수 없으며 구조체의 멤버 함수만 액세스 할 수 있다. 외부에서는 프라이비트 멤버를 읽을 수 없음은 물론이고 존재 자체도 알려지지 않는다.

  • public

    이 속성을 가지는 멤버는 외부로 공개되어 누구나 읽고 쓸 수 있고 함수의 경우는 호출할 수 있다. 구조체가 자신의 속성이나 동작을 외부로 공개하는 수단이 되며 퍼블릭 멤버를 소위 인터페이스라고 한다.

  • protected

    private와 마찬가지로 외부에서는 액세스 할 수 없으나, 상속된 파생 클래스는 이 멤버를 액세스 할 수 있다. 프라이비트 멤버는 파생 클래스에서조차도 참조할 수 없으며 오로지 자신만이 이 멤버를 참조할 수 있다는 점이 다르다.

       

  • 예제 baboAccess

#include <iostream>

using namespace std ;

   

struct babo

{

private :

int a ;

double b ;

char c ;

void initialize() ;

public :

int x ;

int y ;

void func(int i) ;

protected:

float k ;

} ;

   

void main()

{

babo kim ;

// kim.a = 1 ;                에러

kim.x = 10 ;

kim.func(3) ;

// kim.initialize() ;        에러

}

  

   

25.4 클래스

25.4.1 class

C++의 창시자인 스트로스트룹은 확장된 의미의 구조체에 뭔가 멋있고 새로운 이름을 붙여주었는데 그것이 바로 클래스이다.

확장된 구조체와 클래스의 유일한 차이점은 멤버에 대한 디폴트 액세스 지정뿐이다.

구조체도 멤버 함수, 생성자, 파괴자를 가질 수 있고 상속도 가능하며 클래스가 쓰이는 모든 곳에 쓸 수 있다.

구조체의 디폴트 접근 제어 : public

클래스의 디폴트 접근 제어 : private

25.4.2 클래스는 타입이다

C++에서는 구조체의 태그가 타입으로 승격되어 태그로부터 바로 구조체 변수를 선언할 수 있다.

클래스의 이름은 int, double, char 같은 기본형 타입과 동등한 자격을 가지며 사용 방법도 똑같다.

정수형

complex 클래스

C++의 관련 문법

int i ;

complex c ;

클래스의 이름이 타입과 같은 자격을 가진다.

int i = 3 ;

complex c( 1 )

생성자, 선언과 동시에 초기화 할 수 있다.

int i = j ;

complex d = c

복사 생성자, 같은 타입의 다른 객체로부터 생성된다.

i = j ;

d = c ;

대입 연산자

i + j ;

d + c ;

연산자 오버로딩

i = 3.14 ;

complex c(1.2) ;

변환 생성자, 변환 함수

3+i ;

1.0 + c ;

전역 연산자 함수와 프렌드

  • 예제 positionClass

#include <iostream>

#include <time.h>

#include <windows.h>

using namespace std ;

   

class position

{

private :

int x ;

int y ;

char ch ;

public :

void initRand()

{

x = rand()%80 ;

y = rand()%24 ;

ch = rand()%('z'-'a'+1)+'a' ;

}

void outPosition()

{

cout << ch << endl ;

}

void erasePosition()

{

cout<< ' ' << endl ;

}

} ;

   

void main ( void )

{

position arPos[50] ;

position *pPos ;

int i ;

   

srand((unsigned)time(NULL)) ;

for ( i = 0 ; i < sizeof(arPos)/sizeof(arPos[0]) ; i++ )

{

arPos[i].initRand() ;

arPos[i].outPosition() ;

Sleep(500) ;

}

   

Sleep(500) ;

pPos = arPos ;

for ( i = 0 ; i < sizeof(arPos)/sizeof(arPos[0]) ; i++ )

{

pPos->erasePosition() ;

pPos++ ;

Sleep(500) ;

}

}

   

25.4.3 인스턴스

클래스는 어디까지나 타입일 뿐이지 그 자체가 정보를 저장하는 변수는 아니다. 구조체를 선언한다고 해서 구조체 변수가 생기는 것이 아닌 것처럼 클래스를 선언한다고 해서 실제로 값을 기억할 수 있는 메모리가 할당되지는 않는다.

선언문에 의해 생성된 클래스형의 변수를 인스턴스(Instance)라고 한다.

   

영어

번역

의미

인스턴스(Instance)

실체

메모리에 구현되었다.

오브젝트(Object)

객체

독립성을 가진 부품이다.

25.4.4 클래스의 예

  • 예제 timeClass

#include <iostream>

using namespace std ;

   

class time

{

private :

int hour ;

int min ;

int sec ;

   

public :

void setTime(int h, int m, int s)

{

hour = h ;

min = m ;

sec = s ;

}

void outTIme()

{

cout << "현재 시간은 " ;

cout << hour << "시" ;

cout << min << "분" ;

cout << sec << "초 입니다." << endl ;

}

} ;

   

void main ( void )

{

time now ;

   

now.setTime(12,30,40) ;

now.outTIme() ;

}

   

  • 예제 Length

#include <iostream>

using namespace std ;

   

class length

{

private :

double milli ;

   

public :

void setMilli ( double m )

{

milli = m ;

}

double getMilli ( void )

{

return milli ;

}

void outMilli ( void )

{

cout << "길이 = " << getMilli() << "milli" << endl ;

}

void setInch( double i )

{

milli = i*25.4 ;

}

double getInch ( void )

{

return milli/25.4 ;

}

void outInch()

{

cout << "길이 = " << getInch() << "inch" << endl ;

}

} ;

   

void main ( void )

{

length m ;

   

m.setInch( 3 ) ;

m.outMilli() ;

}

 

반응형

'책정리 > 혼자 연구하는 C,C++ 2' 카테고리의 다른 글

29장 상속  (0) 2015.02.28
28장 연산자 오버로딩  (0) 2015.02.27
27장 캡슐화  (0) 2015.02.20
26장 생성자  (0) 2015.02.20
목차  (0) 2015.02.19