Turbo-C
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
터보-C 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
Lua 게시판
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C/C++ 강좌/문서
[39] (10) Templet, Exception, etc. in C++ (1)
남병철.레조 [lezo] 13330 읽음    2009-06-21 12:23


[목차]

(1) Function Templet
(2) Class Templet
(3) Exception
(4) 명시적 형 변환, typedef 및 () 연산자
(5) Standard string Class
(6) 멀티 파일 프로그램










(1) Function Templet

    1. 간단한 템플릿
    각진 괄호안의 class 키워드는 단순히 type이라는 의미로 생각하면 된다.
    class 키워드 다음에 오는 변수는 템플릿 인수라고 불린다.

    컴파일러는 다루게 될 데이타형을 모르기 때문에 템플릿 키워드와 그다음에 오는
    함수 정의를 만나도 아무일하지 않는다. 단지 나중에 사용할 가능성이 있는 템플릿
    이라고만 기억할 것이다. 
    코드 생성은 프로그램의 명령문이 실제로 그 함수를 호출할때까지는 일어나지 않는다.
    함수 템플릿의 템플릿 인수로 int가 오면 템플릿 인수의 자리에 int가 치환되면서
    특정 abs() 함수형을 생성한다. 이것을 함수 템플릿의 인스턴스화라한다.
    각 함수의 인스턴스화 버전을 템플릿 함수라 한다.
    (템플릿 함수는 함수 템플릿의 인스턴스이다. ^^;)
    이렇듯 컴파일러는 새로운 타입으로 템플릿 인수를 접하게되면 각 버전(long,double)의 
    새로운 템플릿 함수가 생성된다. 물론 두번 호출되더라도 하나의 인스턴스만 생성될
    만큼 컴파일러는 똑똑하다.

    즉, 프로그램이 사용하는 RAM의 양은 템플릿을 사용하든 세개의 별도 함수를 작성하든
    동일한것이다. 절약되는 것은 세 개의 별도 함수를 소스 파일에 입력해야 하는 번거로움
    이다. 이는 리스팅을 더 짧고 이해하기 쉽게 만들며 수정또한 한곳에서 가능하다.
    컴파일러는 전적으로 함수 호출의 인수들에서 작동된다.
    즉, 함수 리턴형은 여기서 고려되지 않는다.(함수겹지정과 같음)
    함수 템플릿 코드는 실제 메모리에 놓이지 않고 단지 청사진 또는 패턴의 역할을 한다.
    이것은 클래스가 많은 객체를 만들기위한 청사진이란 것과 같은 의미이다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.29
// 제  목 : 절대값 함수에 사용되는 템플릿
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

template 
T abs( T n )
{
  return ( n < 0 ) ? -n : n;
}
//---------------------------------------------------------------------------
void main()
{
  int int1 = 5;
  int int2 = -6;
  long lon1 = 70000L;
  long lon2 = -80000L;
  double dub1 = 9.95;
  double dub2 = -10.15;

  // 인스턴스화 함수들 호출
  cout << "\nabs(" << int1 << ")=" << abs(int1);  // abs(int)
  cout << "\nabs(" << int2 << ")=" << abs(int2);  // abs(int)
  cout << "\nabs(" << lon1 << ")=" << abs(lon1);  // abs(long)
  cout << "\nabs(" << lon2 << ")=" << abs(lon2);  // abs(long)
  cout << "\nabs(" << dub1 << ")=" << abs(dub1);  // abs(double)
  cout << "\nabs(" << dub2 << ")=" << abs(dub2);  // abs(double)

  getch();
}
//---------------------------------------------------------------------------
2. 여러 인수를 가진 함수 템플릿 예로 템플릿 함수는 이런식으로 이어서 적는것도 가능하다.
    template  int find( atype* array, atype value, int size )
    {
    .....
    }
템플릿 인수들은 반드시 일치해야한다. Ex) 위의 find경우라면..
    find( int*, int, int );
템플릿 함수의 유연성을 증가시키기 위해 둘이상의 템플릿 인수를 둘수있다. 아래와 같이되면 배열의 size도 얼마든지 유연하게 타입을 지정할 수 있다. Ex)
    template 
    btype find( atype* array, atype value, btype size )
    {
      for( btype j = 0; j < size; j++ )
        if( array[j] == value )
          return j;
      return (btype)-1;
    }
[참고] 템플릿 함수를 만들때는 고정된 형에 작용하는 일반 함수로 시작해서 필요한 타입의 설계가 끝나 모든 것이 제대로 작동될 때 함수 정의를 템플릿으로 변환하고 추가되는 형에서 작동하는지 검사한다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.29
// 제  목 : 배열의 특정값을 찾는 함수에 사용되는 템플릿
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

// 함수는 항목의 인덱스 번호 또는 찾지 못할 경우 -1을 리턴한다.
template 
int find( atype* array, atype value, int size )
{
  for( int j = 0; j < size; j++ )
    if( array[j] == value )
      return j;

  return -1;
}

//---------------------------------------------------------------------------

char chrArr[] = { 1, 3, 5, 9, 11, 13 };
char ch = 5;
int intArr[] = { 1, 3, 5, 9, 11, 13 };
int in = 6;
long lonArr[] = { 1L, 3L, 5L, 9L, 11L, 13L };
long lo = 11L;
double dubArr[] = { 1.0, 3.0, 5.0, 9.0, 11.0, 13.0 };
double db = 4.0;

//---------------------------------------------------------------------------
void main()
{
  // 인스턴스화 함수들 호출
  cout << "\n 5  in charArray : index = " << find( chrArr, ch, 6 );
  cout << "\n 6  in intArray  : index = " << find( intArr, in, 6 );
  cout << "\n 11 in lonArray  : index = " << find( lonArr, lo, 6 );
  cout << "\n 4  in cubArray  : index = " << find( dubArr, db, 6 );

  getch();
}
//---------------------------------------------------------------------------
(2) Class Templet 1. 구성원 함수가 외부에 있을때 템플릿 클래스 템플릿은 대개 데이터 저장(컨테이너) 클래스에 사용된다. 또한 구성원 함수가 클래스 사양 외부에 정의되면 새 문법을 사용해야 한다. 템플릿 클래스를 리턴할 경우는 리턴형에도 주의한다.(Int Class일 경우) 함수 인수의 경우는 지정어를 포함할 필요없다. Int Int::xReturn( Int arg ) {}
//---------------------------------------------------------------------------
// 작성일 : 2002.02.24
// 제  목 : 구성원 함수가 외부에 있을때 템플릿
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

//---------------------------------------------------------------------------

const int MAX = 100;

//---------------------------------------------------------------------------

template 
class Stack
{
private:
  Type st[MAX];
  int top;
public:
  Stack();
  void push( Type var );
  Type pop()
  {
    return st[top--];
  }
};

//---------------------------------------------------------------------------

template 
Stack::Stack()
{
  top = -1;
}

template 
void Stack::push( Type var )
{
  st[++top] = var;
}

//---------------------------------------------------------------------------

void main()
{
  Stack s1;

  s1.push( 1111.1 );
  s1.push( 2222.2 );
  s1.push( 3333.3 );

  cout << "1: " << s1.pop() << endl;
  cout << "2: " << s1.pop() << endl;
  cout << "3: " << s1.pop() << endl;

  Stack s2;

  s2.push( 123123123L );
  s2.push( 234234234L );
  s2.push( 345345345L );

  cout << "1: " << s2.pop() << endl;
  cout << "2: " << s2.pop() << endl;
  cout << "3: " << s2.pop() << endl;

  getch();
}

//---------------------------------------------------------------------------
2. 템플릿을 이용한 링크 리스트 Class 뿐만아니라 Struct 또한 템플릿으로 지정한다. [링크 확장 구조] first = NULL; [ ]->NULL | first [ ]->[ ]->NULL | first 자체 Class 나 Struct 내에는 linklist와 link등의 이름만 사용하는것이 가능하다. 하지만 외부 구성원 함수에서는 linklist과 link처럼 이름과 템플릿 인수를 사용해야한다.
//---------------------------------------------------------------------------
// 작성일 : 2002.02.24
// 제  목 : 템플릿을 이용한 Link List
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

// 구조체 link
template 
// 목록의 한 요소
struct link
// within this struct def 'link' means link
{
  TYPE data;
  link* next;
};

//---------------------------------------------------------------------------

// 클래스 linklist
template 
// 링크의 목록
class linklist
// 이 클래스 안에서 정의 'linklist'는 linklist을 의미함
{
  private:
  // 첫 번째 링크 포인터
  link* first;
  public:
  linklist()
  {
    // 첫 번째 링크가 없음
    first = NULL;
  }
  // 데이터 항목 추가( 1개 링크 )
  void additem( TYPE d );
  // 모든 링크를 화면 출력
  void display();
  ~linklist();
};

//---------------------------------------------------------------------------

template 
// 데이터 항목 추가
void linklist::additem( TYPE d )
{
  // 새 링크를 만든다.
  link* newlink = new link;
  // 그것에 데이터를 줌
  newlink->data = d;
  // 다음 링크를 가리킴
  newlink->next = first;
  // 이제 첫번째 링크가 이것을 가리킴
  first = newlink;
}



template 
// 모든 링크를 화면 출력
void linklist::display()
{
  // 포인터를 첫 번째 링크로 설정
  link* current = first;
  // 마지막 링크에서 종료
  while( current != NULL )
  {
    // 데이터를 출력
    cout << endl << current->data;
    // 다음 링크로 이동
    current = current->next;
  }
}

// 파괴자
template 
// 모든 링크를 삭제
linklist::~linklist()
{
  // 마지막 링크에서 종료
  while( first != NULL )
  {
    // 'temp'는 현재 링크를 가리킴
    link* temp = first;
    // 'first'는 다음 링크를 가리킴
    first = temp->next;
    // 현재 링크를 삭제
    delete temp;
  }
}

//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char* argv[])
{

  linklist ld;

  ld.additem( 151.5 );
  ld.additem( 262.6 );
  ld.additem( 373.7 );
  ld.display();

  linklist lch;

  lch.additem( 'a' );
  lch.additem( 'b' );
  lch.additem( 'c' );
  lch.display();

  getch();

  return 0;

}
//---------------------------------------------------------------------------
3. 템플릿을 이용한 사용자 정의 데이터형 저장하기 사용자 정의 데이터를 받아드리기위해 연산자를 겹지정한다.
//---------------------------------------------------------------------------
// 작성일 : 2002.02.26
// 제  목 : 템플릿에 사용자 정의 데이터형 저장하기
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

//---------------------------------------------------------------------------

const int LEN = 80;

//---------------------------------------------------------------------------

class employee
{
  private:
    char name[LEN];
    unsigned long number;
  public:
    friend istream& operator >> ( istream& s, employee& e );
    friend ostream& operator << ( ostream& s, employee& e );
};

//---------------------------------------------------------------------------

istream& operator >> ( istream& s, employee& e )
{
  cout << "\nEnter last name : "; cin >> e.name;
  cout << "Enter number : "; cin >> e.number;
  return s;
}

ostream& operator << ( ostream& s, employee& e )
{
  cout << "\nName : " << e.name;
  cout << "\nNumber : " << e.number;
  return s;
}

//---------------------------------------------------------------------------

template 
struct link
{
  TYPE data;
  link* next;
};



template 
class linklist
{
  private:
    link* first;
  public:
    linklist() { first = NULL; }
    void additem( TYPE d );
    void display();
    ~linklist();
};



template 
void linklist::additem( TYPE d )
{
  link* newlink = new link;
  newlink->data = d;
  newlink->next = first;
  first = newlink;
}



template 
void linklist::display()
{
  link* current = first;
  while( current != NULL )
  {
    cout << endl << current->data;
    current = current->next;
  }
}



template 
linklist::~linklist()
{
  while( first != NULL )
  {
    link* temp = first;
    first = temp->next;
    delete temp;
  }
}

//---------------------------------------------------------------------------

void main()
{
  linklist lemp;
  employee emptemp;
  char ans;

  do
  {
    cin >> emptemp;
    lemp.additem( emptemp );
    cout << "\nAdd another (y/n)? ";
    cin >> ans;
  } while( ans != 'n' );
  lemp.display();

  getch();
}

//---------------------------------------------------------------------------
(3) Exception 1. 예외처리의 필요성 - 기존의 C 언어 프로그램에서, 오류는 흔히 그것이 발생한 함수에서 특정값을 리턴하는 것으로 신호를 받는다. * if ... else 문을 넣고 오류를 처리하면서 명령문을 사용하면 리스팅이 뒤얽혀서 판독하기 어렵다. * 일부 함수의 오류처리용 리턴값은 실용적이지 못하다. Ex) min() : 리턴될 수 있는 모든 값이 유용한 값이다. * Class 생성자에 오류가 발생한 사실을 어떻게 알 수 있을까? (생성자는 암시적으로 호출된다.) * Class 공급업체와 그 Class를 사용해서 응용프로그램을 만드는 업체사이에 함수 호출로 오류 값을 주고 받기가 어렵다. 2. 예외처리를 위해 C++에 추가된 키워드 try, catch, throw * Class 사용자는 추가된 이들 키워드 뿐만아니라 예외 클래스라는 새로운 종류의 엔티티를 만들게 될 것이다. * 예외가 발생하는 이벤트 순서 - 코드는 try 블록 밖에서 정상적으로 실행한다. - 제어권이 try 블록으로 들어간다. - try 블록의 명령문이 구성원 함수에서 오류를 야기한다. - 구성원 함수가 예외를 전달한다. - 제어권이 try 블록 다음에 오는 예외 블록(catch 블록)으로 넘어간다. * 위의 이벤트 순서에 맞춘 예제 스택에서 Overflow 및 Underflow를 예외 처리하는 예제
//---------------------------------------------------------------------------
// 작성일 : 2002.03.10
// 제  목 : 예외처리 기본 골격
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
const int MAX = 3;  // int 3개를 보관하는 스택

//---------------------------------------------------------------------------

class Stack
{
private:
  int st[MAX];
  int top;

public:
  class Range {};

  Stack()
  {
    top = -1;
  }

  void push( int var )
  {
    if( top >= MAX - 1 )
      throw Range();
    st[++top] = var;
  }

  int pop()
  {
    if( top < 0 )
      throw Range();
    return st[top--];
  }
};

//---------------------------------------------------------------------------

void main()
{
  Stack s1;

  try
  {
    s1.push( 11 );
    s1.push( 22 );
    s1.push( 33 );
//    s1.push( 44 );
    cout << "1: " << s1.pop() << endl;
    cout << "1: " << s1.pop() << endl;
    cout << "1: " << s1.pop() << endl;
//    cout << "1: " << s1.pop() << endl;
  }

  catch( Stack::Range )
  {
    cout << "Stack Full or Empty" << endl;
  }

  cout << "Arrive here after catch ( or normal exit )" << endl;
  getch();
}
//---------------------------------------------------------------------------
3. 예외의 심층적인 탐구 * 다중예외 - 예외의 상황에 맞게 원하는 만큼 예외를 만들 수 있다. (catch 블록은 try 블록 다음에 와야한다.) (catch 블록은 break하지 않아도 선택된 하나만 실행된다.)
//---------------------------------------------------------------------------
// 작성일 : 2002.03.13
// 제  목 : 다중 예외처리
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
const int MAX = 3;  // int 3개를 보관하는 스택

//---------------------------------------------------------------------------

class Stack
{
private:
  int st[MAX];
  int top;

public:
  class Full {};
  class Empty {};

  Stack()
  {
    top = -1;
  }

  void push( int var )
  {
    if( top >= MAX - 1 )
      throw Full();
    st[++top] = var;
  }

  int pop()
  {
    if( top < 0 )
       throw Empty();
    return st[top--];
  }
};

//---------------------------------------------------------------------------

void main()
{
  Stack s1;

  try
  {
    s1.push( 11 );
    s1.push( 22 );
    s1.push( 33 );
//    s1.push( 44 );
    cout << "1: " << s1.pop() << endl;
    cout << "1: " << s1.pop() << endl;
    cout << "1: " << s1.pop() << endl;
    // Stack Empty 출력
    cout << "1: " << s1.pop() << endl;
  }

  catch( Stack::Full )
  {
    cout << "Stack Full" << endl;
  }

  catch( Stack::Empty )
  {
    cout << "Stack Empty" << endl;
  }
  getch();
}
//---------------------------------------------------------------------------
* 예외 Class에서 데이타 읽어오기 - 예외처리기가 예외를 포착할때(throw) 객체의 데이터를 검색할 수 있다는데 착안 - 예외 Class의 데이터를 예외 처리기에서 직접 접근할 수 있도록 Public으로 만들면 편리하다.
//---------------------------------------------------------------------------
// 작성일 : 2002.03.19
// 제  목 : 예외처리기가 예외를 포착할때(throw) 객체의 데이터를 검색
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

class Distance
{
  private:
    int feet;
    float inches;

  public:
    //--------------------------------
    // 예외 Class
    class InchesEx
    {
      public: // catch구문에서 접근을 용이하게 하기위해 public 사용
        char origin[80];
        float iValue;

        InchesEx( char* or, float in )
        {
          strcpy( origin, or );
          iValue = in;
        }
    };
    //--------------------------------

    Distance()
    {
      feet = 0;
      inches = 0.0;
    }

    Distance( int ft, float in )
    {
      if( in >= 12.0 )
        throw InchesEx( "2-arg constructor", in );

      feet = ft;
      inches = in;
    }

    void getdist()
    {
      cout << "\nEnter feet : ";
      cin >> feet;
      cout << "Enter inches : ";
      cin >> inches;
      if( inches >= 12.0 )
        throw InchesEx( "getdist() function", inches );
    }

    void showdist()
    {
      cout << feet << "\'-" << inches << '\"';
    }
  // End of public
};

//---------------------------------------------------------------------------

void main()
{
  try
  {
    Distance dist1( 17, 3.5 );
    Distance dist2;
    dist2.getdist();

    cout << "\ndist1 = ";
    dist1.showdist();

    cout << "\ndist2 = ";
    dist2.showdist();

    getch();
  }
  catch( Distance::InchesEx ix )
  {
    cout << "\nInitialization error in " << ix.origin
         << ".\Inches value of " << ix.iValue
         << " is too large.";
  }
}

//---------------------------------------------------------------------------
* xalloc Class를 사용한 예외처리
//---------------------------------------------------------------------------
// 작성일 : 2002.03.22
// 제  목 : new 로 메모리 할당할때 발생하는 예외처리
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

void main()
{

  const unsigned int MAX = 900000000;
  char* ptr;
  char* ptr2;
  unsigned int j;

  try
  {
    ptr = new char[MAX];
    // new 를 사용하는 다른 명령문들
//    ptr2 = new char[MAX];
  }
  catch( xalloc )
  {
    cout << "\nxalloc exception : can't allocation memory.";
    exit(1);
  }

  // 메모리를 데이터로 채움
  for( j = 0; j < MAX; j++ )
    *( ptr + j ) = j % 128;

  for( j = 0; j < MAX; j++ )
  {
    if( *(ptr + j) != j%128 )
    {
      cout << "\nData error";
      exit(1);
    }
  }
  delete[] ptr;
  cout << "\nMemory use is successful.";

  getch();

}

//---------------------------------------------------------------------------
* 그 밖의 예외처리 특징 - 예외가 전달되면 try 블록의 해당 지점까지 코드가 작성한 모든 객체의 파괴자가 자동으로 호출된다. 즉, 예외 메커니즘은 적어도 객체가 존재하는 한 try 블록의 코드가 '초기화'되게 한다. - 예외처리로 try블록이 초기화되는 것을 이용하면 프로그램을 종료 시키지 않거나 오류에서 복구를 시도할 수 있을것이다. 예를들어, 대개 루프에 try 및 catch 블록을 삽입하여 try 블록의 시작위치로 제어 권이 시작되게 시도할 수 있을것이다. 만일, 전달된 예외와 일치하는 예외 처리기가 없으면 프로그램은 무조건 종료된다. - try 블록에 내포된 함수 내부에서 예외가 발생해도 상위의 try - catch 블록이 예외를 처리한다. - 예외는 예외가 발생한 전달지점으로 복귀가 불가능하다. 오히려 goto에 더 가깝다.(사실 C언어의 longjmp와 더 가깝다) 전달된 예외는 catch(오류처리기)로 넘어갔다가 catch 블록 다음에 오는 코드로 내려간다.
Lyn [tohnokanna]   2009-06-21 12:26 X
현재는 class 키워드 대신 typename 키워드가 더 일반적이더군요  : )

+ -

관련 글 리스트
39 (10) Templet, Exception, etc. in C++ (1) 남병철.레조 13330 2009-06-21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.