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

C/C++ 강좌/문서
[36] (07) Virtual, Friend in C++ (2)
남병철.레조 [lezo] 9002 읽음    2009-06-21 12:13


[목차]

(1) 가상함수를 이용한 다형성
(2) 추상 Class와 가상 파괴자
(3) RTTI (RunTime Type Identification)
(4) 프렌드 함수
(5) 프렌드 Class
(6) 내포 Class와 정적 멤버 데이타










(4) 프렌드 함수
    겹지정은 실제로 일어나는 일을 위장한다.
    겹지정의 + 연산자는 사실 왼쪽의 객체에서 호출되고 오른쪽의 객체를 인수로 취하는
    구성원 함수일 뿐이다.

    컴파일러는 + 연산자를 다음과 같은 시각으로 처리한다.
    [코딩]
    at2 = at1 + 3;
    [컴파일러 시각]
    at2 = at1.operator+(3);
    하지만 정수가 왼쪽이 있다고 생각해보라. 답이없다. 코딩이 불가능하다.
    이것을 해결하는 방법은 프렌드이다.

    friend는 마치 일반(비구성원) 전역함수인 것처럼 클래스나 다른 함수의 외부에
    정의할 수 있다.
    friend 선언문은 클래스의 어디에나 놓일 수 있다. 적어도 컴파일러에 대해서는
    public에 놓이든 private 부분에 놓이든 상관없다.
    하지만 개념적으로 public class interface의 일부이므로 public에 놓인다.
    즉, 모든 Class 사용자는 friend 함수를 호출할 수 있다.
    보다시피 아래의 friend 함수는 airtime의 구성원 함수가 아니다.(정의부분을 보라)
    그럼에도 불구하고 airtime의 private 데이터에 접근할 수 있다.
    이것은 friend로 선언되었기 때문에 가능한 것이다.

    friend 함수는 다른 전역함수와 마찬가지로 main에서 호출된다.
    그럼 friend 함수의 동작을 살펴보자.
    일단 인수는 언제나 구성원 함수보다 하나더 가진다. 즉 함수의 왼쪽과 오른쪽을 인수로
    가지는 것이다. 양쪽중 어느 하나라도 airtime 객체이면 friend함수로 호출된다.
    만약 왼쪽만 airtime이고 오른쪽이 정수이고 구성원함수로 +가 있으면 아래의 소스처럼
    표기법을 분명히해야 컴파일러가 제대로 작동한다. 그렇지 못하면 아래와 같은 오류가 뜬다.
    Ambiguity between 'operator +(airtime,airtime)' and 'airtime::operator +(airtime)'

    Class를 살펴보면 알겠지만 friend는 양쪽인수를 모두 받는 전역함수이고 구성원 함수는
    오른쪽 인수만 있을때 작동하게된다. 즉, friend만 있으면 구성원함수가 없어도 모든 기능을
    작동하게된다. ^-^v
//---------------------------------------------------------------------------
// 작성일 : 2001.11.11
// 제  목 : friend 사용
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include 
#include 
#pragma hdrstop
//---------------------------------------------------------------------------
class airtime
{
private:
  int hours;
  int minutes;

public:
  airtime( int h = 0, int m = 0 ) : hours( h ), minutes( m ) {}

  void display()
  {
    cout << hours << ':' << minutes;
  }

  void get()
  {
    char dummy;
    cin >> hours >> dummy >> minutes;
  }

  airtime operator+( airtime right )
  {
    airtime temp;
    temp.hours = hours + right.hours;
    temp.minutes = minutes + right.minutes;
    if( temp.minutes >= 60 )
    {
      temp.hours++;
      temp.minutes -= 60;
    }
    cout << "General operator+\n";
    return temp;
  }

  friend airtime operator+( airtime, airtime ); // Declare Statment
};

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

airtime operator+ ( airtime left, airtime right )
{
  airtime temp;
  temp.hours = left.hours + right.hours;
  temp.minutes = left.minutes + right.minutes;
  if( temp.minutes >= 60 )
  {
    temp.hours++;
    temp.minutes -= 60;
  }
  cout << "\nFriend operator+\n";
  return temp;
}

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

void main()
{
  airtime at1, at2;

  cout << "Enter an airtime : ";
  at1.get();

  // 구성원 + 이용
//  at2 = at1 + 3;
  at2 = at1.operator +(3);
  cout << "airtime + 3 = ";
  at2.display();

  // friend 이용
  at2 = 3 + at1;
  cout << "3 + airtime = ";
  at2.display();

  // at2의 1인수 생성자를 호출하게될 뿐이다.
  at2 = 3 + 5;
  cout << "\n\n3 + airtime = ";
  at2.display();

  getch();
}

//---------------------------------------------------------------------------
(결과)
Enter an airtime : 1:1
General operator+
airtime + 3 = 4:1
Friend operator+
3 + airtime = 4:1

3 + 5 = 8:0
//---------------------------------------------------------------------------
[규칙 위반에 대한 논쟁] friend 함수는 자신이 접근할 수 있는 데이터를 가진 클래스 안에서 friend로 선언되어야 한다. 즉, Class의 소스 코드에 접근하지 못하는 프로그래머는 함수를 friend로 만들 수 없다. Class의 무결성은 여전히 보호된다. * friend를 함수표기법으로 사용하기 English객체(이 강의내용의 처음부터 피트.인치를 저장하는 객체로 사용한것)를 제곱해서 float형으로 표현하는것을 구성원함수를 사용하는것보다 friend를 이용하는것이 더 직관적인 이유를 알아보자. 구성원 함수를 사용하게 되면 English객체를 제곱하는 코드는 아래와 같다. float sqft = dist.square(); 하지만 friend를 사용하면 하나의 인수를 받는 함수로 바꿀 수 있다. sqft = square(dist); 보다시피 구성원 함수보다 하나의 인수를 더 갖게된다. 또한 더 직관적이다. 일반함수로 friend의 효과를 나타내기 위해서는 알다시피 private 데이타에 접근하기 위해 구성원함수를 만들어놓지 않는이상 불가능하며 그만큼 번거롭다. 하지만 friend는 그러한 Class 특징의 번거로움을 모두 극복하면서 마치 일반함수에 작동하는 객체인것처럼 조작할 수 있다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.12
// 제  목 : 일반 함수처럼 보이게 friend 사용
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

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

class English
{
private:
  int feet;
  float inches;

public:
  English( int ft, float in )
  {
    feet = ft;
    inches = in;
  }
  void showdist()
  {
    cout << feet << "\'-" << inches << '\"';
  }
  friend float square( English );
};

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

float square( English d )
{
  float fltfeet = d.feet + d.inches/12;
  float feetsqrd = fltfeet * fltfeet;
  return feetsqrd;
}

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

void main()
{

  English dist( 3, 6.0 );

  float sqft = square(dist);

  cout << "\nDistance = ";
  dist.showdist();
  cout << "\nSquare = " << sqft << " square feet";

  getch();
}

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

(결과)
Distance = 3'-6"
Square = 12.25 square feet

//---------------------------------------------------------------------------
* friend 함수로 클래스간 통신 두 Class가 무관하다면 아마도 friend 함수는 두 Class의 객체를 인수로 취하고 그 private 데이타에 작용할 것이다. 아래의 예제는 friend 함수가 두 Class간의 다리 역할을 할 수 있다는 것을 보여주는 간단한 예제인 Bridge를 나타낸다. friend 함수는 일반 전역함수처럼 Class 내부에 선언되고 외부에 하나의 정의부분이 있을뿐 이다. Class는 선언할 때까지는 참조할 수 없다는 것을 명심한다. Class beta는 Class alpha에서 함수 frifunc()의 선언문에 참조되므로, beta는 alpha 앞에 선언되어야 한다. 그래서 alpha앞에 class beta;를 선언했던것이다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.12
// 제  목 : friend 함수로 Class 간의 통신
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

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

class beta;
class alpha
{
private:
  int data;

public:
  alpha()
  {
    data = 3;
  }
  friend int frifunc( alpha, beta );
};

class beta
{
private:
  int data;

public:
  beta()
  {
    data = 7;
  }
  friend int frifunc( alpha, beta );
};

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

int frifunc( alpha a, beta b )
{
  return( a.data + b.data );
}

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

void main()
{

  alpha aa;
  beta bb;
  cout << frifunc( aa, bb );

  getch();
}

//---------------------------------------------------------------------------
(결과)
10
//---------------------------------------------------------------------------
(5) 프렌드 Class 프렌드 Class를 사용하는 이유는 대개 Class간 통신을 촉진시키는 것이다. [Class간 통신에 friend를 사용할 경우] 두 Class는 한 Class가 다른 Class의 private 데이타에 직접 접근해야 할(public접근 함수를 사용하지 않고)정도로 밀접한 관계가 있다. 각 Class의 데이타는 보안을 위해 private으로 만들려고하고 어느 Class도 다른것의 동족이 아니므로 상속은 배제한다. 이러할때 한 Class가 다른 Class의 개별 구성원에 접근하는 방법으로 프렌드를 사용한다. 그 외에도 많은 잇점이 있다. ** friend로 전에 정의된 Class의 개별 구성원에 접근하기 ** 전에 정의된 Class에 접근하려면 이전 Class에 friend로 접근할 수 있게 하고싶은 Class를 선언한다. 아래의 예는 단순히 접근하는 예를 보여주는것 뿐 실제로 사용하기에는 부족하다. alpha의 private에 접근하려면 실객체 않에서 가능하다.
class alpha
{
private:
  friend class beta;
  int adata;
public:
  void Display()
  {
    adata = 6;
  }
};

class beta
{
public:
  void bfunc()
  {
    alpha objA;
    cout << objA.adata << endl;
  }
};
** friend로 아직 정의되지 않은 구성원에 접근하기 ** 함수 정의를 Class 밖에해서 private에 접근하는 방법이다. alpha에서 beta의 private에 접근할때도 역시 실객체 않에 있어야 접근이 가능하다.
class alpha
{
public:
  void afunc();
};

class beta
{
private:
  friend class alpha;
  int bdata;
};

void alpha::afunc()
{
  beta objB;
  objB.bdata = 3;
};
** 포인터를 이용한 Class간 통신 ** Class alpha가 2개의 beta 포인터를 갖고 beta의 private에 접근하게 된다. 물론 양쪽 Class에는 friend Class가 선언되어있다. Class beta가 하나의 alpha 포인터를 갖고 alpha에 접근하게 된다. private에 생성자를 넣어 friend Class에서만 beta를 생성할 수 있게 함으로써 사용자의 오용을 막을 수 있다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.20
// 제  목 : 포인터를 이용한 Class간 통신
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------
class alpha
{
private:
  friend class beta;
  beta* bptr1;
  beta* bptr2;
  int adata;
public:
  alpha();
  void afunc();
  void Display();
};
//---------------------------------------------------------------------------
class beta
{
private:
  friend class alpha;
  alpha* aptr;
  int bdata;
  beta( alpha* ap ) : aptr( ap )
  {}
public:
  void bfunc()
  {
    aptr->adata = 3;
  }
};
//---------------------------------------------------------------------------
alpha::alpha()
{
  bptr1 = new beta( this );
  bptr2 = new beta( this );
}
//---------------------------------------------------------------------------
void alpha::afunc()
{
  bptr1->bdata = 4; // alpha --> beta에 접근
  bptr2->bdata = 5; // alpha --> beta에 접근
  bptr2->bfunc();   // beta --> alpha에 접근
}
//---------------------------------------------------------------------------
void alpha::Display()
{
  cout << "Alpha adata = " << this->adata  << "  beta-->alpha" << endl;
  cout << "Beta1 bdata = " << bptr1->bdata << "  alpha-->beta" << endl;
  cout << "Beta2 bdata = " << bptr2->bdata << "  alpha-->beta" << endl;
}
//---------------------------------------------------------------------------
void main()
{
  alpha objA;

  objA.afunc();
  objA.Display();

  getch();
}
//---------------------------------------------------------------------------
(결과)
Alpha adata = 3  beta-->alpha
Beta1 bdata = 4  alpha-->beta
Beta2 bdata = 5  alpha-->beta
//---------------------------------------------------------------------------
** friend를 이용한 경마게임 1 ** [작성방식] - horse Class를 track Class의 friend로 만들어 경주마가 개별 트랙 데이터에 접근할 수 있도록 하는 것이다. 또한 Class 사용자가 트랙이 없으면 경주마 객체를 만들거나 사용할 수 없게 하기위해 horse Class의 생성자와 구성원 함수를 private 으로 만든다. - Class 사용자는 한 트랙 객체를 만들고 트랙은 경주마를 만든다. 트랙과 경주마의 통신은 포인터와 friend 관계를 이용한다. [참고] 대표적 경마 거리의 단위(영어권)는 1/8마일(201.17m)인 펄롱이다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.21
// 제  목 : friend Class간 통신을 바탕으로한 경마게임
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include       // gotoxy() 등
//#include         // delay()
//#include      // random()
//#include        // randomize()
#pragma hdrstop

const int CPF = 5;      // 화면 열/펄롱
//---------------------------------------------------------------------------
class track
{
private:
  friend class horse;   // 경주마가 데이터에 접근하게 함
  horse* hptr;          // 경주마 메모리 포인터
  int total;            // 총 경주마 수
  int count;            // 지금까지 만든 경주마
  int track_length;     // 펄롱 단위의 트랙 길이
  float elapsed_time;   // 경주를 출발한 이후의 시간
public:
  track( float, int );
  void track_tick();    // 틱 단위의 시간, 전체 트랙
  ~track();
};
//---------------------------------------------------------------------------
class horse
{
private:
  friend class track;   // track이 함수에 접근하게 함
  track* ptr_track;     // track 포인터
  int horse_number;     // 이 경주마의 번호
  float finish_time;    // 이 경주마의 최종 시간
  float distance_run;   // 출발한 이후의 거리
                        // 주 : 개별 구성원 함수

  horse() : distance_run( 0.0 )   // 경주마를 만든다.
  {}
  void horse_init( track* pt )    // 경주마 초기화
  {
    ptr_track = pt;
    horse_number = (ptr_track->count)++;
  }

  void horse_tick();              // 한 경주마의 시간
};
//---------------------------------------------------------------------------
void horse::horse_tick()          // 각 경주마에 대해
{
  // 경주마와 번호를 화면에 출력
  gotoxy( 1 + int(distance_run * CPF), 2 + horse_number * 2 );
  cout << " >>" << horse_number << "(@)";
  if( distance_run < ptr_track->track_length + 1.0 / CPF )
  {
    // 약 1에서 3틱을 건너뜀
    if( random(3) )
      distance_run += 0.2;    // 0.2 펄롱 전진

    finish_time = ptr_track->elapsed_time;  // 최종 시간을 화면 출력
  }
  else
  {
    int mins = int( finish_time ) / 60;
    int secs = int( finish_time ) - mins * 60;
    gotoxy( 6 + int( distance_run * CPF ), 2 + horse_number * 2 );
    cout << " Time = " << mins << ":" << secs << endl;
  }
}
//---------------------------------------------------------------------------
// 2인수 생성자
track::track( float l, int t ) : track_length( l ), total( t ),
                                 count( 0 ), elapsed_time( 0.0 )
{
  randomize();
  clrscr();

  // 트랙을 화면 출력
  for( int f = 0; f <= track_length; f++ )  // 각 펄롱에 대해
    for( int r = 1; r <= total * 2 + 1; r++ ) // 각 화면 행에 대해
    {
      gotoxy( f * CPF + 5, r );
      if( f == 0 || f == track_length )
        cout << "[]"; // 출발선 또는 결승선
      else
        cout << '|';  // 펄롱 표시기
    }

  // 경주마를 만든다.
  hptr = new horse[total];  // 모든 경주마의 메모리 확보
  for( int j = 0; j < total; j++ )  // 각경주마를
    (hptr+j)->horse_init( this );   // 트랙 포인터로 초기화
}
//---------------------------------------------------------------------------
void track::track_tick()
{
  elapsed_time += 1.75;
  for( int j = 0; j < total; j++ )  // 각 경주마에대해
    (hptr+j)->horse_tick();         // 경주마를 새로 고침
}
//---------------------------------------------------------------------------
track::~track()
{
  delete[] hptr;
}
//---------------------------------------------------------------------------
void main()
{
  float length;
  int nhorses;
  cout << "\nEnter track length (furlongs, 6 to 12) : ";
  cin >> length;
  cout << "\nEnter number of horses (1 to 10) : ";
  cin >> nhorses;
  track t(length, nhorses);
  while( !kbhit() )
  {
    t.track_tick();
    Sleep( 500 );
  }
  t.~track();

  getch();
}
//---------------------------------------------------------------------------
(6) 내포 Class와 정적 멤버 데이타 ** friend를 이용한 경마게임 2 (Nested Class 이용) ** 1. Nested Class의 의미 [특징] beta Class가 alpha Class에서만 사용되고 alpha 외부에서는 결코 접근되거나 인스턴스화하지 않는다면, 간단히 alpha안에 넣어버리면 된다. 일반 Class의 이름과 구성원 이름은 전역의 범위를 가지고 있다. alpha안에 beta가 내포되어있다면 beta의 구성원 이름을 프로그램의 다른곳에서 다른목적으로 사용해도 충돌의 우려가 없다. 항상 전역 이름 공간의 이름 수를 최소화하는 것은 좋은 습관이다. 내포클래스 관계에서 단지 내포되었다는 이유만으로 서로의 데이타에 자동으로 접근 할 수 없다. 역시 friend Class를 사용한다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.25
// 제  목 : friend Class간 통신을 바탕으로한 경마게임
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------
class alpha
{
private:
  int adata;
  ////////////////////
  class beta
  {
  private:
    int bdata;
    friend class alpha;
  public:
    void bfunc();
    {
      alpha objA;
      objA.adata = 3;
      cout << "alpha adata = " << objA.adata << endl;
      cout << " beta adata = " << this->bdata << endl;
    }
  };
  ////////////////////
  friend class alpha::beta; // 반드시 beta선언 뒤에 와야한다.

public:
  void afunc()
  {
    beta objB;
    objB.bdata = 2;
    objB.bfunc();
  }
};
//---------------------------------------------------------------------------
    void beta::bfunc()
    {
      alpha objA;
      objA.adata = 3;
      cout << "alpha adata = " << objA.adata << endl;
      cout << " beta adata = " << this->bdata << endl;
    }
void main()
{
  alpha objA;
  objA.afunc();

  getch();
}
//---------------------------------------------------------------------------
(결과)
alpha adata = 3
 beta adata = 2
//---------------------------------------------------------------------------
2. Nested Class를 이용한 경마게임
//---------------------------------------------------------------------------
// 작성일 : 2001.11.25
// 제  목 : friend Class간 통신을 바탕으로한 경마게임
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include         // gotoxy() 등
//#include         // delay()
//#include      // random()
//#include        // randomize()
#pragma hdrstop

const int CPF = 5;        // 화면 열/펄롱
class horse;
//---------------------------------------------------------------------------
class track
{
private:
  ////////// 내포클래스(Nested Class) //////////
  class horse
  {
  private:
    track* ptr_track;       // track 포인터
    int horse_number;       // 이 경주마의 번호
    float finish_time;      // 이 경주마의 최종 시간
    float distance_run;     // 출발 이후의 거리
  public:
    horse() : distance_run( 0.0 ) {}    // 경주마를 만든다.
    void horse_init( track* pt )        // 경주마를 초기화
    {
      ptr_track = pt;
      horse_number = (ptr_track->count)++;
    }
    void horse_tick();      // 경주마 하나에 대한 시간 기록
  };
  //////////////////////////////////////////////
  horse* hptr;                // horse 메모리 포인터
                              // (반드시 horse 클래스 뒤에 와야 함)
  int total;                  // 총 경주마 수
  int count;                  // 지금까지 만든 경주마 수
  int track_length;           // 펄롱 단위의 트랙 길이
  float elapsed_time;         // 경주를 출발한 이후의 시간
  friend class track::horse;

public:
  track( float, int );        // 2인수 생성자
  void track_tick();          // 전체 트랙의 시간 기록
  ~track()                    // 파괴자
  {
    delete[] hptr;            // 경주마 메모리를 삭제
  }
};
//---------------------------------------------------------------------------
track::track( float l, int t ) : track_length( l ), total( t ),
                                 count( 0 ), elapsed_time( 0.0 )
{
  randomize();
  clrscr();

  for( int f = 0; f <= track_length; f++ )      // 각 펄롱에 대해
  {
    for( int r = 1; r <= total * 2 + 1; r++ )   // 각 화면 행에 대해
    {
      gotoxy( f*CPF + 5, r );
      if( f == 0 || f == track_length )
        cout << "[]";   // 출발선과 결승선
      else
        cout << '|';    // 펄롱 표시기를 그린다.
    }
  }
  hptr = new horse[ total ];        // 경주마를 만든다.
  for( int j = 0; j < total; j++ )  // 각 경주마를
    (hptr+j)->horse_init(this);     // 트랙 포인터로 초기화
}

void track::track_tick()
{
  elapsed_time += 1.75;             // 시간을 새로 고침

  for( int j = 0; j < total; j++ )  // 각 경주마에 대해
    (hptr+j)->horse_tick();         // 경주마를 새로 고침
}

void track::horse::horse_tick()   // 각 경주마에 대해
{                                 // 경주마와 번호를 화면 출력
  gotoxy( 1 + int(distance_run * CPF ), 2 + horse_number * 2 );
  cout << " >>" << horse_number << "(@)";
  if( distance_run < ptr_track->track_length + 1.0 / CPF )
  {
    if( random( 3 ) )                       // 약 1에서 3틱을 건너뜀
      distance_run += 0.2;                  // 0.2 펄롱 전진
    finish_time = ptr_track->elapsed_time;  // 최종 시간을 새로 고침
  }
  else
  { // 최종 시간을 화면 출력
    int mins = int( finish_time ) / 60;
    int secs = int( finish_time ) - mins * 60;
    gotoxy( 6 + int( distance_run * CPF ), 2 + horse_number * 2 );
    cout << " Time=" << mins << ":" << secs;
  }
}
//---------------------------------------------------------------------------
void main()
{
  float length;
  int nhorses;

  cout << "\nEnter track length (furlongs) : ";
  cin >> length;
  cout << "\nEnter number of horses ( 1 to 10 ) : ";
  cin >> nhorses;

  track t(length, nhorses);   // 트랙과 경주마를 만든다.

  while( !kbhit() )           // 키를 누르면 종료
  {
    t.track_tick();           // 모든 경주마를 이동하고 화면 출력
    Sleep( 500 );
  }
  t.~track();

  getch();
}
//---------------------------------------------------------------------------
** friend를 이용한 경마게임 3 (Static Data 이용) ** 경마게임에서 트랙 관련 데이터(트랙 길이, 경과 시간, 총 경주마 수)를 별도의 클래스로 만들 필요는 없다. 트랙은 모든 경주마에 대해 공통이므로 정적 데이터와 함수로 나타낼 수 있다. 정적 구성원은 전체 Class에 대해 한 번만 발생하므로, horse Class의 모든 구성원에 공통인 데이터와 함수를 나타낼 수 있다.
//---------------------------------------------------------------------------
// 작성일 : 2001.11.25
// 제  목 : friend Class간 통신을 바탕으로한 경마게임
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include         // gotoxy() 등
//#include         // delay()
//#include      // random()
//#include        // randomize()
#pragma hdrstop

const int CPF = 5;        // 펄롱당 화면 열 수
//---------------------------------------------------------------------------
class horse
{
private:
  // 트랙 특성(선언 전용)
  static horse* hptr;             // horse 메모리 포인터
  static int total;               // 총 경주마 수
  static int count;               // 지금까지 만들어진 경주마 수
  static int track_length;        // 펄롱 단위의 트랙 길이
  static float elapsed_time;      // 경주를 출발한 이후의 시간

  // 경주마 특성
  int horse_number;               // 이 경주마의 번호
  float finish_time;              // 이 경주마의 최종 시간
  float distance_run;             // 출발한 이후의 거리
  // 주 : 개별 경주마 관련 구성원 함수
  horse() // 각 경주마를 위한 생성자
  {
    horse_number = count++;       // 경주마의 번호를 설정
    distance_run = 0.0;           // 아직 이동되지 않았음
  }
  void horse_tick();              // 한 경주마의 시간 기록

public:
  static void init_track( float l, int t );   // 트랙을 초기화
  static void track_tick();                   // 전체 트랙의 시간 기록
  static void kill_track()                    // 모든 경주마를 삭제
  {
    delete[] hptr;
  }
};

// 정적(track) 변수 정의
horse* horse::hptr;                           // 정적(track) 변수를 정의
int horse::total;
int horse::count = 0;
int horse::track_length;
float horse::elapsed_time = 0.0;

void horse::init_track( float l, int t )      // 정적(track) 함수
{
  total = t;                        // 경주마 수를 설정
  track_length = l;                 // 트랙 길이를 설정
  randomize();                      // 난수를 초기화
  clrscr();                         // 화면을 지움

  // 트랙을 화면 출력
  for( int f = 0; f <= track_length; f++ )    // 각 펄롱에 대해
  {
    for( int r = 1; r <= total * 2 + 1; r++ ) // 각 화면 행에 대해
    {
      gotoxy( f * CPF + 5, r );
      if( f == 0 || f == track_length )
        cout << "[]";       // 출발선
      else
        cout << '|';        // 결승선
    }
  }
  hptr = new horse[total];  // 모든 경주마의 메모리 확보
}

void horse::track_tick()    // 정적(track) 함수
{
  elapsed_time += 1.75;     // 시간을 새로 고침

  for( int j = 0; j < total; j++ )  // 각 경주마에 대해
    (hptr+j)->horse_tick();         // 경주마를 새로 고침
}

void horse::horse_tick()            // 각 경주마에 대해
{ // 경주마와 번호를 화면 출력
  gotoxy( 1 + int(distance_run * CPF), 2 + horse_number * 2 );
  cout << " >>" << horse_number << "(@)";
  if( distance_run < track_length + 1.0 / CPF ) // 결승점까지
  {
    if( random(3) )               // 약 1에서 3틱을 건너뜀
      distance_run += 0.2;        // 0.2 펄롱 전진
    finish_time = elapsed_time;   // 최종 시간을 새로 고침
  }
  else
  { // 최종 시간을 화면 출력
    int mins = int(finish_time) / 60;
    int secs = int(finish_time) - mins * 60;
    gotoxy( 6 + int(distance_run * CPF), 2 + horse_number * 2 );
    cout << " Time = " << mins << ":" << secs;
  }
}
//---------------------------------------------------------------------------
void main()
{
  float length;
  int nhorses;

  cout << "\nEnter track length (furlongs) : ";
  cin >> length;
  cout << "\nEnter number of horses (1 to 10) : ";
  cin >> nhorses;

  // 트랙과 경주마를 초기화
  horse::init_track( length, nhorses );

  while( !kbhit() )
  {
    horse::track_tick();  // 모든 경주마를 이동하고 화면 출력
    Sleep( 500 );         // 0.5초 기다림
  }
  horse::kill_track();    // 메모리에서 경주마를 삭제

  getch();
}
//---------------------------------------------------------------------------

+ -

관련 글 리스트
36 (07) Virtual, Friend in C++ (2) 남병철.레조 9002 2009/06/21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.