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

C/C++ 강좌/문서
[40] (11) Templet, Exception, etc. in C++ (2)
남병철.레조 [lezo] 37243 읽음    2009-06-21 12:26


[목차]

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










(4) 명시적 형 변환, typedef 및 () 연산자

    - ANSI/ISO C++ 초안은 형 변환을 수행하는 새로운 방법을 소개했다.
      이유중 하나는 큰 리스팅에서 형변환을 찾기 어려웠다는 것이다.

    - 구식 C방식의 형 변환
      intvar = (int)longvar;

    - 새로운 C++함수 방식의 형 변환
      intvar = int(longvar);

    - C++에 새로 추가된 4개의 형변환 키워드
      static_cast
      dynamic_cast
      const_cast
      reinterpret_cast
      형 변환은 C++ 형 지정 시스템을 깨뜨리므로 버그의 원인이 되기 쉬운데, 그것을
      찾을 수 없으면 고치기가 더욱 어려워진다.


    * static_cast
      static_cast는 구식 형 변환을 대신해서 사용한다.
      예) intvar = int(longvar);              // 정보 손실이 가능한 형변환
          intvar = static_cast(longvar);
//---------------------------------------------------------------------------
// 작성일 : 2002.04.03
// 제  목 : static_cast 형 변환
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

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

class Base {};

class Derv : public Base {};

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

void main()
{
  int intvar = 27;
  long longvar = 12341234L;

  longvar = intvar; // 좋음
  longvar = static_cast(intvar); // 좋음&명시적임

  intvar = longvar; // 컴파일러 경고
  intvar = int(longvar); // 좋음(하지만 위험)
  intvar = static_cast(longvar); // 좋음(하지만 위험)

  Base base;
  Base* bptr;

  Derv derv;
  Derv* dptr;

  bptr = &derv; // 좋음(형 격상)
  bptr = static_cast(&derv); // 좋음(형 격상)&명시적임

//  dptr = &base; // 컴파일러 오류(형 격하)
  dptr = (Derv*)&base; // 좋음(하지만 위험)
  dptr = static_cast(&base); // 좋음(하지만 위험)

  getch();

}

//---------------------------------------------------------------------------
* dynamic_cast - dynamic_cast는 RTTI의 대안이다. 기초 Class 배열을 훑어보면서 특정 유도 Class 형의 객체를 찾을 경우 유용하다. 이 형 변화는 객체가 원하는 형이 아니면 0을 리턴하고 원하는 형이면 객체 포인터를 리턴한다. - dynamic_cast는 기초 Class가 다형적이어야 한다는데 주의한다. (즉, 적어도 하나의 가상 함수를 포함해야한다.) 기초 Class 배열 하나하나에 하위 클래스들을 이것 저것 집어놓은 상황에서는 서로다른 객체형에 서로 다르게 반응하기 위해 dynamic_cast(typeid의 RTTI 접근 방식)가 필요할 때도 있다.
//---------------------------------------------------------------------------
// 작성일 : 2002.04.03
// 제  목 : dynamic_cast 형 변환
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

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

class Base
{
  public:
    void virtual dummy() {}
};

class Derv1 : public Base {};

class Derv2 : public Base {};

class Derv : public Base {};

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

void main()
{
  Base* bptrs[10];

  Derv1 derv1;
  Derv2 derv2;

  bptrs[0] = &derv1;
  bptrs[1] = &derv2;

  Derv1* d1ptr;
  Derv2* d2ptr;

  d1ptr = dynamic_cast( bptrs[0] );
  cout << "\nd1ptr = " << d1ptr;
  d2ptr = dynamic_cast( bptrs[1] );
  cout << "\nd2ptr = " << d2ptr;

  d1ptr = dynamic_cast( bptrs[1] );
  cout << "\nd1ptr = " << d1ptr;
  d2ptr = dynamic_cast( bptrs[0] );
  cout << "\nd2ptr = " << d2ptr;

  getch();

}

//---------------------------------------------------------------------------
[결과]

d1ptr = 0021FF88
d2ptr = 0012FF84
d1ptr = 00000000
d2ptr = 00000000

//---------------------------------------------------------------------------
* const_cast - const 형 변환은 '상수 성질을 없애는 형 변환'을 가능하게 해준다. (의미가 모호한 C++용어중의 하나이다.) - const 변수에 직접 const_cast를 사용할 수 없고 변수 포인터나 참조에만 사용할 수 있다. const 변수 포인터나 참조가 변수를 수정해야 하는 함수에 인수로 전달되면 가장 유용할 것이다.
//---------------------------------------------------------------------------
// 작성일 : 2002.04.16
// 제  목 : const_cast 형 변환
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

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

void main()
{

  int var = 27;
  const int constvar = 33;

  var = constvar;
  // 오류, const를 수정
  // constvar = var;

  int* ptr = &var;
  // 오류, 포인터는 const가 아님
  // int* ptr = &constvar;
  const int* ptr_c = &constvar;

  // 오류, 포인터는 const가 아님
  // ptr = ptr_c;
  ptr = const_cast( ptr_c );

  getch();

}

//---------------------------------------------------------------------------
* reinterpret_cast - 명시적 형변환 중에서 가장 강력하고 또한 가장 위험한 형변환이다. 포인터를 정수로, 부동소수점 수를 비트패턴으로 바꾸는 등 아주 많은 것을 다른 것으로 변환할 수 있다. 아래의 예제는 int[10]의 배열을 가진 struct를 int*로 캐스팅하여 자료의 조작을 보여주는 예이다. 자료형은 다르지만 그 메모리상의 변지수가 같으므로 캐스팅을 통해 서로 같은 영역을 조작한 후에 원상태로 캐스팅하게되면 원래의 자료형 값인 struct가 int*로 조작되어 0으로 변형된 것을 볼 수 있다.
//---------------------------------------------------------------------------
// 작성일 : 2002.04.24
// 제  목 : reinterpret_cast 형 변환
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

using namespace std;
const int sz = 10;
 
struct X 
{ 
  int a[sz]; 
};
 
void print(X* x) 
{
  for(int i = 0; i < sz; i++)
    cout << x->a[i] << ' ';
  cout << endl << "--------------------" << endl << endl;
}

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

int main(int argc, char *argv[])
{
  X x;
  cout << "struct X's size : " << sizeof( x ) << endl;
  cout << "struct X's address : " << &x << endl;
  cout << "struct X's member a[0]'s address : " << &x.a[0] << endl << endl;
  cout << "Before Cast" << endl;
  print(&x);

  cout << "int* xp = reinterpret_cast(&x)" << endl;
  int* xp = reinterpret_cast(&x);
  cout << "&x == &x.a[0] == xp " << endl << &x << " == " << &x.a[0] << " == " << xp << endl << endl;
  cout << "xp == &x.a[0] : " << *xp << endl << endl;

  // 각 int* 마다 0을 대입 (초기화)
  cout << "xp initialize = 0 step sizeof(int)" << endl;
  // 포인터 연산에서 +1의 증가는 해당 포인터가 가리키는 곳의 자료형만큼의 건너뜀이다.( xp + sz )
  for(int* i = xp; i < xp + sz; i++)
    *i = 0;

  // Can't use xp as an X* at this point
  // unless you cast it back:
  print(reinterpret_cast(xp));
  // In this example, you can also just use
  // the original identifier:
  print(&x);
}
//---------------------------------------------------------------------------
* typedef - 데이터형을 다루는 또 다른 C++의 특징으로 typedef 지정자가 있다. [특징] 1. 리스팅이 좀더 명백해질 수 있다. Ex) typedef int error_number; 2. 여러 단어도 적용된다. Ex) typedef unsigned char byte; 3. 기존의 형 이름을 새로운 형 이름으로 만든다. (Class 사양처럼 새로운 형을 만드는것은 아니다.) Ex) typedef int* ptr_error_number; ptr_error_number pen1, pen2, pen3; [참고] #define 지시자는 한 그룹의 문자를 또 다른것으로 그냥 대체한다. 즉, 위의 예처럼 포인터를 define으로는 잘 처리하지 못한다. 위와 같은 경우 define으로 하면 pen2, pen3에는 *가 붙지 않은 것이다. 4. 긴 형 이름을 단순화 하는데 사용된다. Ex) class Universal_Amalgamated_Corporation_Employee_Class {...}; Universal_Amalgamated_Corporation_Employee_Class emptemp; ->typedef Universal_Amalgamated_Corporation_Employee_Class Uemploee; Uemploee emptemp; 5. 코드를 서로 다른 상황에 더 잘 대응하게 만드는 것이다. Ex) 16비트 운영체제에서는 int가 16비트이지만 32비트 운영체제에서는 short가 16비트이다. typedef int int16; // 16비트 시스템에서 typedef short int16; // 32비트 시스템에서 int16 var1, var2; 6. template이 여러 인수를 가지거나 인수들이 클래스일 경우 template Class의 선언문이 지나치게 비대해 지는것을 막는다. Ex) template class employee {...}; employee tempemp; ->typedef employee part_time_machinist; part_time_machinist tempmach1; [단점] 선언문의 정의를 보기위해 리스팅의 윗부분(헤더파일)으로 다시 가서 typedef내용을 살펴야 실제 의미를 파악할 수 있으므로 프로그램 전체의 가독성은 떨어지게된다. * 함수 연산자의 겹지정() - 함수 연산자는 소괄호 상이다. () - 함수 연산자는 함수가 선언 또는 호출되고 있다는 것을 컴파일러에 알리기 위해 사용한다. - 겹지정은 Class객체를 함수처럼 작동시키려는 특별한 상황에서 유용하다. [용도] multiply객체를 함수 객체라 한다. 그것을 호출한 모든 프로세스에 별도의 버전을 가진 함수를 만들 수 있다. 함수가 호출을 기억해야 하는 데이터(호출된 횟수나 마지막 생성한 난수 등)를 포함하고 있는 다중화 작업 환경에서 중요하다. 각 프로세스가 한 함수를 호출하고 이 데이터와 접촉하면 문제가 발생할 수 있다. 그러나 각 프로세스가 자체의 함수 객체를 만들면, 데이터의 자체 버전을 갖게된다. 함수 객체는 인수에 객체를 지정하는 또 다른 함수의 인수로 함수를 지정해야 할 때에도 유용하다.
//---------------------------------------------------------------------------
// 작성일 : 2002.05.10
// 제  목 : ()연산자를 겹지정
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

class alpha
{
public:
	long operator() ( int j, int k )
	{
		return static_cast( j ) * static_cast( k );
	}
};

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

int main(int argc, char *argv[])
{
	alpha multiply;

	// 객체를 함수처럼 사용
	double answer = multiply( 2002, 3003 );
	cout << setprecision( 10 ) << answer << endl;
}

//---------------------------------------------------------------------------
(5) Standard string Class * 생성자와 연산자 - 다양한 생성자를 사용하여 string 객체를 만들 수 있다.
//---------------------------------------------------------------------------
// 작성일 : 2002.05.27
// 제  목 : 생성자와 연산자
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

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

int main(int argc, char *argv[])
{
	// C 문자열
	string s1( "IN HOC SIGNO VINCES" );
	// 반복되는 문자열 (ASCII 65번 문자열을 '-'회수 만큼 반복) : ASCII DEC 45 = '-', ASCII DEC 47 = '/'
	string s2( '/', 45 );
	// 위치 7에서 시작(0부터 7까지)
	string s3( s1, 7 );
	// 위치 13부터 6문자
	string s4( s1, 13, 6 );

	// 문자열들을 화면 출력
	cout << "     01234567890123456789012345678901234567890123456789" << endl;
	cout << "s1 = " << s1 << endl;
	cout << "s1 = " << s1.length() << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s2 = " << s2.length() << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s3 = " << s3.length() << endl;
	cout << "s4 = " << s4 << endl;
	cout << "s4 = " << s4.length() << endl;
	
	// 기본 생성자
	string s5;
	cout << "Before assignment, length of s5 = " << s5.length() << endl;

	// 대입
	s5 = s1;
	cout << "After  assignment, length of s5 = " << s5.length() << endl << endl;

	// 반복되는 문자열 (ASCII 65번 문자열을 '-'회수 만큼 반복) : ASCII DEC 45 = '-', ASCII DEC 47 = '/'
	string s6( '/', 45 );
	cout << "s6 = " << s6 << endl;
	cout << "Length of s6 = " << s6.length() << endl;
	cout << "s2 = " << s2 << endl;
	s6[0] = 'X';
	s6[46] = 'X';

	cout << "s6 = ";
	for( int j = 0; j < 47; j++ )
		cout << s6[j];
	cout << endl;

	// 연쇄
	string s7 = s1 + " (Motto of the Roman Empire)";
	cout << s7.length() << endl;
	cout << "s7 = " << s7 << endl;
	cout << "s1[3] = " << s1[3] << endl;

	return 0;
}
//---------------------------------------------------------------------------
* 구성원 함수 - insert( pos, ptr ); 해당 객체에서 pos부터 시작하여 char* 문자열 ptr을 객체에 추가한다. - erase( pos, n ); 해당 객체에서 pos부터 시작하여 n문자를 삭제한다. - replace( pos, n, ptr ); 해당 객체에서 pos부터 시작하여 n문자를 삭제한 후 char* 문자열 ptr을 그 위치에 추가. - find( ptr, pos ); 해당 객체에서 pos부터 시작하여 char* 문자열 ptr의 패턴의 첫번째 결과의 위치를 리턴한다. - find_first_of( ptr, pos ); 해당 객체에서 문자!를 찾는다.(문자열이 아님). 찾고자하는 문자를 잘 모를 경우에 유용하다. - substr( pos, n ); 해당 객체에서 pos부터 시작하여 n문자로 이루어진 새로운 string 객체를 리턴한다.
//---------------------------------------------------------------------------
// 작성일 : 2002.05.27
// 제  목 : 구성원 함수
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

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

int main(int argc, char *argv[])
{
	string s1( "Don told Estelle he would get the ring." );
	cout << "s1 = " << s1 << endl;

	// "get" 앞에 "not "을 추가
	s1.insert( 26, "not " );
	cout << "s1 = " << s1 << endl;

	// "not "을 삭제
	s1.erase( 26, 4 );
	// "Estelle"를 "Pam"으로 대체
	s1.replace( 9, 7, "Pam" );
	cout << "s1 = " << s1 << endl;

	// "Pam"을 찾음
	int loc1 = s1.find( "Pam", 0 );
	cout << "Pam is at = " << loc1 << endl;

	// loc1 뒤의 첫 번째
	// 공백 문자를 찾음
	int loc2 = s1.find_first_of( " \t\n", loc1 );

	// 부속 문자열 "Pam"을 만든다.
	string s2 = s1.substr( loc1, loc2-loc1 );
	cout << "Hi, " << s2 << endl;
	
	return 0;
}
//---------------------------------------------------------------------------
* string 객체를 인수로 전달 - copy( ptr, n, pos ); 해당 객체에서 pos부터 시작하여 n문자를 char* 문자열 ptr에 복사한다. - c_str() 해당 string 객체를 const char* 문자열로 변환한다. 즉, string 객체에 있는 char 배열 포인터 및 끝에 '\0'을 포함하여 리턴한다.
//---------------------------------------------------------------------------
// 작성일 : 2002.05.28
// 제  목 : 구성원 함수
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

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

int main(int argc, char *argv[])
{
	// 함수 기본형 선언
	string func( string );
	string s1( "IN HOC SIGNO VINCES" );
	string s2 = func( s1 );

	// 리턴값을 화면 출력
	cout << "main is displaying : " << s2 << endl;

	char char_arr[80];
	int len = s1.length();

	int n = s1.copy( char_arr, len, 0 );
	char_arr[len] = '\0';
	cout << "characters copied = " << n << endl;
	cout << "char_arr = " << char_arr << endl;

	const char* ptr = s1.c_str();
	cout << "ptr = " << ptr << endl;
		
	return 0;
}

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

string func( string s )
{
	cout << "func is displaying argument : " << s << endl;
	return string( "return value from func\n" );
}

//---------------------------------------------------------------------------
* string 객체의 배열 - xstring.compare( string ); 리턴값 > 0 : string이 xstring보다 사전식 순으로 앞선 단어이다. 리턴값 = 0 : 동일한 문자열 리턴값 < 0 : string이 xstring보다 사전식 순으로 뒤에 온다.
//---------------------------------------------------------------------------
// 작성일 : 2002.05.28
// 제  목 : 구성원 함수
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 

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

int main(int argc, char *argv[])
{
	const int SZ = 10;
	string new_name;
	// 문자열 객체의 비밀
	string arr[SZ+1] = { "Adam", "Bob", "Clair", "Doug", "Emily", "Frank", "Gail", "Harry", "Ian", "Joe" };

	cout << "Enter a name : ";
	cin >> new_name;
	
	int j = 0;
	// 배열의 각 이름 검사
	while( j < SZ+1 )
	{
		int len = arr[j].length();
		int lex_comp = new_name.compare( arr[j] );
		// 새 문자열이 더 크면(앞선 문자열이면) 다음 문자열과 비교
		if( lex_comp > 0 )
			j++;
		else
		{
			for( int k = SZ-1; k >= j; k-- )
				arr[k+1] = arr[k];
			arr[j] = new_name;
			break;
		}
	}
	if( j == SZ+1 )
		arr[10] = new_name;

	for( j = 0; j < SZ+1; j++ )
		cout << arr[j] << endl;
		
	return 0;
}
//---------------------------------------------------------------------------
(6) 멀티파일 프로그램 - namespace namespace에 사용된 이름은 전체 가시성을 갖지 않고, 그 범위가 이름 공간으로 제한된다. Ex) // 파일 A namespace NS_George { class alpha {}; } // 파일 B namespace NS_Harry { class alpha {}; } 물론 같은 파일에 위 와같이 다른 이름공간을 사용할 수 있다. 자체의 namespace 안에서는 흔히 하던 대로 alpha를 참조할 수 있다. 그러나 namespace 밖에서는 참조하는 alpha 클래스가 어느 것인지를 나타내어야 한다. 1. scope 연산자(::)를 사용한다. Ex) namespace NS_Harry { NS_George::alpha GAlpha; // NS_George alpha 객체를 만든다. class alpha {}; } 2. using 지시자를 사용한다. Ex) namespace NS_Harry { class alpha {}; using NS_George::alpha; ... alpha GAlpha1; // NS_George alpha 객체를 만든다. ... } 2번의 예에서 NS_Harry는 NS_George의 namespace에 있는 모든 요소에 접근할 수 있다.

+ -

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