[목차]
(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에 있는 모든 요소에 접근할 수 있다.