template <typename T>

template <class T>

이 두 문장은 똑같다.

템플릿을 이용하면, 자료형에 의존적이지 않은 함수를 만들 수 있다. 

즉, define SQUARE(x)     ((x)*(x)) 와 똑같이 된다. 그리고 define은 전처리기에 의해 처리가 되어서 오류를 잡지 못하지만 inline은 컴파일러에 처리가 되어서 오류를 잡을 수 있으니 얼마나 좋은가~~


실행 결과



Posted by scii
:

'Programming > C++' 카테고리의 다른 글

namespace (이름 공간)  (0) 2013.03.17
template을 이용한 inline 함수  (0) 2013.03.17
C++ Language Tutorial 사이트  (0) 2012.11.20
dynamic_cast : Polymorphic 클래스 기반의 형 변환  (0) 2012.10.31
C++ 에서의 형 변환 연산  (0) 2012.10.31
Posted by scii
:
Posted by scii
:

# 상속관계에 놓여있는 두 클래스 사이에서, 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환할 경우에는 dynamic_cast 연산자를 사용한다.


# 상속관계에 놓여있는 두 클래스 사이에서, 기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형 데이터로 형 변환할 경우에는 static_cast 연산자를 사용한다.



하지만, 다음의 조건만 만족하면!! dynamic_cast 연산자도 기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형으로의 형 변환을 허용한다.


"기초 클래스가 'Polymorphic Class' 이다."



Polymorphic 클래스란, 하나 이상의 가상함수를 지니는 클래스를 뜻한다. 그러니 상속관계에 놓여있는 두 클래스 사이에서 기초 클래스에 가상함수가 하나 이상 존재하면, dynamic_cast 연산자를 이용해서 기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형 데이터로 변환이 가능하다.


 

13행: Simple 클래스가 Polymorphic 클래스이므로 Complex 클래스도 Polymorphic 클래스이다.

24행: 포인터 변수가 가리키는 것은 Complex 객체이다.

25행: 기초 클래스인 Simple이 Polymorphic 클래스이므로 dynamic_cast 연산자로 형 변환이 가능하다.


위의 main함수에서 dynamic_cast 연산이 성공한 이유는?


-> 포인터 변수 simPtr이 실제 가리키는 객체가 Complex 객체이기 때문이다. 즉, 포인터 변수 simPtr이 가리키는 객체를 Complex 형 포인터 변수 comPtr이 함께 가리켜도 문제되지 않기 때문에 성공한 것이다.



만약, 안정적이지 못한 형 변환을 시도할 경우, dynamic_cast는 NULL 포인터가 반환된다.



이렇듯 dynamic_cast는 안정적인 형 변환을 보장한다. 특히 컴파일 시간이 아닌 실행 시간에(프로그램이 실행중이 동안에) 안전성을 검사하도록 컴파일러가 바이너리 코드를 생성한다는 점에 주목할 필요가 있다. 

물론 이로 인해서 실행속도는 늦어지지만, 그만큼 안정적인 형 변환이 가능한 것이다. 그리고 이러한 특성 때문에 연산자의 이름이 dynamic으로 시작하는 것이다.


static_cast는 안전성을 보장하지 않는다. 컴파일러는 무조건 형 변환이 되도록 바이너리 코드를 생성하기 때문에, 그로 인한 실행의 결과는 전적으로 프로그래머가 책임져야 한다. 그래서 이러한 특정 때문에(실행중인 동안에 안전성 검사를 진행하지 않는 특성때문에) 연산자의 이름이 static으로 시작하는 것이다. 

물론, 실행속도는 빠르다. 실행 시간에 안전성 검사를 별도로 진행하지 않기 때문이다. 






bad_cast 예외


- 프로그래머가 정의하지 않아도 발생하는 예외.

bad_cast 예외는 dynamic_cast 연산자를 이용한 형 변환의 과정에서 발생할 수 있는 예외이다.




29행: 참조자 ref가 실제 참조하는 대상이 Simple 객체이기 때문에 Complex 참조형으로의 형 변환은 안전하지 못하다. 그리고 참조자를 대상으로는 NULL을 반환할 수 없기 때문에 이러한 상황에서는 bad_cast 예외가 발생한다.


※ 위 예제에서 보이듯이, 참조형을 대상으로 dynamic_cast 연산을 진행할 경우에는 bad_cast 예외가 발생할 수 있기 때문에 반드시 이에 대한 예외처리를 해야 한다.


Posted by scii
:

C++ 에서 C 스타일의 형 변환 연산자를 가리켜 '오래된 C 스타일 형 변환 연산자(Old C-style cast operator)' 라 부른다. 이렇듯 C 스타일의 형 변환 연산자는 C언어와의 호환성을 위해서 존재할 뿐, C++에서는 새로운 형 변화 연산자와 규칙을 제공하고 있다 .



40행: 문제가 없어 보이기도 하다. 하지만 기초 클래스의 포인터 형을 유도 클래스의 포인터 형으로 형 변환하는 것은  일반적인 경우의 형 변환이 아니다. 


이 예제의 문제점은 40행에 있다. 이것이 실수인지 아니면 일부러 이렇게 했는지 그 의도를 모르기 판단하기 힘들기 때문이다. 


그래서 이러하 유형으 논란과 문제점 때문에 C++ 에서는 다음과 같이 총 4개의 연산자를 추가로 제공하면서 용도에 맞는 형 변환 연산자의 사용을 유도하고 있다. 


static_cast

const_cast

dynamic_cast

reinterpret_cast


위의 형 변환 연산자들을 사용하면 프로그래머는 자신이 의도한 바를 명확히 표시할 수 있다. 따라서 컴파일러도 프로그래머의 실수를 지적해 줄 수 있고, 코드를 직접 작성하지 않은 프로그래머들도 코드를 직접 작성한 프로그래머의 실수여부를 판단할 수 있다. 







dynamic_cast : 상속관계에서의 안전한 형 변


dynamic_cast<T>(expr)

즉, <> 사이에 변환하고자 하는 자료형의 이름을 두되, 객체의 포인터 또는 참조형이 와야 하며, () 사이에는 변환의 대상이 와야 한다. 


※ dynamic_cast 연산자를 사용하겠다는 것은 다음의 의미가 담겨있다.

- 상속관계에 있는 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하겠습니다.




※ 43행의 형 변환은 의도적으로라도 하면 안된다. 그러나 40행 같은 경우에는 경우에 따라서 필요할 것도 같다. 이러한 경우엔 어떻게 해야 하나?

- 이러한 형 변환을 의도적으로 진행한다는 것을 명시하기 위하 형 변환 연산자를 사용해야 한다.  그리고 그에 맞는 형 변환 연산자가 바로 static_cast 연산자이다.







static_cast : A타입에서 B타입으로


static_cast<T>(expn)

이것은, 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로뿐만 아니라, 기초 클래스의 포인터 및 참조형 데이터도 유도 클래스의 포인터 및 참조형 데이터로 아무런 조건 없이 형 변환시켜 준다.

하지만 그데 대한 책임은 프로그래머에게 있다. 

 



43행 같은 경우에는 어떻게 해서든 정당화될 수 없는 상황이다. 따라서 이러한 형태로 문장을 구성하면 안된다. 하지만 static_cast는 dynamic_cast와 달리 이것 또한 허용한다. 


즉, static_cast 연산자는 dynamic_cast 연산자와 달리, 보다 많은 형 변환을 허용한다. 하지만 그에 따른 책임도 프로그래머가 져야 하기 때문에 신중하게 선택해야 한다. 

dynamic_cast 연산자를 사용할 수 있는 경우에는 dynamic_cast 연산자를 사용해서 안전성을 높여야 하며, 그 이외의 경우에는 정말 책임질 수 있는 상황에서만 제한적으로 static_cast 연산자를 사용해야 한다. 


※ 보다 빠른 static_cast 연산자

- dynamic_cast 연산자보다 static_cast 연산자를 사용했을 때 연산의 속도가 더 빠르다. 따라서 이러한 이유로 dynamic_cast 연산자를 사용해도 되는 상황에서 조차 static_cast 연산자를 사용하는 경우도 적지 않다.



static_cast 연산자는 기본 자료형 데이터간의 형 변환에도 사용이 된다. 




static_cast 연산자는 '기본 자료형 간의 형 변환'과 '클래스의 상속관계에서의 형 변환'만 허용을 하지만, C언어의 형 변환 연산자는 다음과 같이 말도 안되는 형 변환도 허영하기 때문에 여전히 static_cast 연산자의 사용은 의미를 갖는다.



위에서 보인 형 변환은 static_cast 연산자로는 불가능한 형 변환이다. 즉, 여전히 static_cast 연산자는 C언어의 형 변환 연산자보다 적은 것을 허용하고 있으며, 이로 인해서 static_cast 연산자를 보는 순간 다음과 같이 판단할 수 있다.


"상속관계에 있는 클래스의 포인터 및 참조형 데이터의 형 변환인가? 아니면 기본 자료형 데이터의 형 변환인가?"







const_cast : const의 성향을 삭제하라.


C++에서는 포인터와 참조자의 const 성향을 제거하는 형 변환을 목적으로 다음의 형 변환 연산자를 제공하고 있다.

const_cast<T>(expn)



참조자, 포인터의 const 성향을 제거하는 것이 가치를 떨어뜨릴 수 있다. 허나, 다른 이면에선 나름의 의미를 발견할 수 있다. 위의 예제처럼.


이렇듯 const_cast 형 변환 연산은, 함수의 인자전달 시 const 선언으로 인한 형의 불일치가 발생해서 인자의 전달이 불가능한 경우에 유용하게 사용이 된다. 


※ 위 예제에서 보이는 것과 같이 const_cast 연산자의 긍정적인 측면이 잘 드러나는 경우에만 제한적으로 사용해야 한다.

const_cast 연산자는 volatile(컴파일러의 최적화를 제한하는 목적) 의 성향을 제거하는데도 사용할 수 있다.







reinterpret_cast : 상관없는 자료형으로의 형 변환


reinterpret_cast 연산자는 전혀 상관이 없는 자료형으로의 형 변환에 사용이 되며, 기본적인 형태는 다음과 같다.

reinterpret_cast<T>(expn)


reinterpret_cast 연산자는 포인터를 대상으로 하는, 그리고 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다.



7행: int 형 정수에 바이트 단위 접근을 위해서 int형 포인터를 char형 포인터로 형 변환하고 있다.

10행: 바이트 단위 데이터를 문자가 아닌 정수의 형태로 출력하기 위해서 char형 데이터를 int형으로 변환하고 있다.



reinterpret_cast 연산자는 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다고 했다. 

따라서 다음과 같은 문장의 구성도 가능하다.



위 예제의 경우에 크게 의미를 부여하기 어렵지만, 특정 상황에서는 이러한 유형의 연산이 유용하게 사용되기도 하며, 이는 reinterpret_cast 연산자가 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다는 사실을 뒷받침하기도 한다.


Posted by scii
:

new 연산자에 의해서 발생하는 예외


- new 연산에 의한 메모리 공간의 할당이 실패하면 bad_alloc이라는 예외가 발생한다. 

bad_alloc은 헤더파일 <new> 에 선언된 예외 클래스로써 메모리 공간의 할당이 실패했음을 알리는 의도로 정의되었다.



위 예제에서 보이듯이 프로그래머가 정의하지 않아도 발생하는 예외도 있다. 






모든 예외를 처리하는 catch 블록



마지막 catch 블록에 덧붙여지는 경우가 많은데, 대신 catch의 매개변수 선언에서 보이듯이, 발생한 예외와 관련해서 그 어떠한 정보도 전달받을 수 없으며, 전달된 예외의 종류도 구분이 불가능하다. 







예외 던지기


- catch 블록에 전달된 예외는 다시 던져질 수 있다. 

그리고 이로 인해서 하나의 예외가 둘 이상의 catch블록에 의해서 처리되게 할 수 잇다. 




예외처리는 가급적 간결한 구조를 띠는게 좋다. 따라서 정말로 필요한 상황이 아니라면, 굳이 예외를 다시 던지기 위해서 노력할 필요는 없다. 


Posted by scii
:

클래스의 객체도 예외 데이터가 될 수 있다. 그리고 이것이 일반적인 방법이다.



예외 클래스와 예외 객체


예외발생을 알리는데 사용되는 객체를 가리켜 '예외객체'라 하며, 예외객체의 생성을 위해 정의된 클래스'예외 클래스'라 한다.

그리고 객체를 이용해서 예외상황을 알리면, 예외가 발생한 원인에 대한 정보를 보다 자세히 담을 수 있다.



54행: 예외객체를 전달하는 일반적인 방법을 보이고 있다. 어차피 예외객체는 C++의 예외처리 메커니즘에 의해 처리되기 때문에 코드상에서 이를 직접 참조할 필요가 없다. 

따라서 예외객체는 임시객체의 형태로 생성하는 것이 보통이다.


예외 클래스라고 해서 특별히 다른 것은 없다. 다만, 해당 예외상황을 잘 표현할 수 있도록 정의하면 된다.

그리고 너무 복잡하게 정의하지 않는 것이 좋다. 예외의 표현을 위한 최소한의 기능만 담아서 정의하면 된다.







상속관계에 있는 예외 클래스


- 예외 클래스도 상속의 관계를 구성할 수 있다.




56행: 상속에 의해서 DepositException 객체도 AccountException 객체로 간주가 되기 때문에 이러한 선언이 가능하다.


위에서 보이듯이 상속을 통해서 예외 클래스를 묶으면, 예외의 처리를 단순화시킬 수 있다. 물론 이것이 항상 좋은 것은 아니지만, 단순화해도 되는 상황에서는 유용하게 활용될 수 있다.







예외의 전달방식에 따른 주의사항 


- 예외가 발생하면, 해당 예외 데이터를 전달받을 수 있는 catch 블록을 위에서 아래로 찾아내려 간다. 그리고 적절한 catch 블록을 찾게 되면, 해당 catch 블록이 실행되면서 예외의 처리는 완료가 된다.


# catch 블록이 잘못된 예


위 예제의 문제점은 모든 예외 객체가 AAA 클래스를 상속하기 때문에, 45행의 catch블록이 실행된다는데 있다. 

즉, 위와 같은 구조로 catch 블록을 구성하면, 50, 55행의 catch 블록은 실행되지 않는다. 

내가 원하던 것은 "BBB 예외객체는 catch(BBB& expn) 블록에 의해서 처리가 되고, CCC 예외객체는 catch(CCC& expn) 블록에 의해서 처리가 되게끔 하고 싶었던 것이다."



내가 원하던대로 하게 하려면, catch블록의 배치를 변경해야 한다.




BBB 객체는 일종의 AAA 객체이지만, AAA 객체는 일종의 BBB 객체가 아니다. CCC도 마찬가지.

그렇기 때문에 원하는 결과를 얻을 수 있다.



Posted by scii
:

자료형이 일치하지 않아도 예외 데이터는 전달된다.


int Simple(void)

{

...

try

{

if(...)

throw -1;    // int형 예외 데이터

}


catch(char expn)    {...}    // char 형 예외 데이터를 전달하라.

}


이 경우, 자료형의 불일치로 인해서 예회는 처리되지 않는다.(catch 블록으로 값이 전달되지 않는다.) 따라서 Simple 함수를 호출한 영역으로 예외 데이터가 전달된다.







하나의 try 블록과 다수의 catch 블록


- 하나의 try 블록 내에서 유형이 다른 둘 이상의 예외상황이 발생할 수도 있고, 이러한 경우 각각의 예외를 표현하기 위해 사용되는 예외 데이터의 자료형이 다를 수 있기 때문에, try 블록에 이어서 등장하는 catch 블록은 둘 이상이 될 수 있다.








전달되는 예외의 명시


- 함수 내에서 발생할 수 있는 예외의 종류도 함수의 특징으로 간주된다. 따라서 이미 정의된 특정 함수의 호출을 위해서는 함수의 이름, 매개변수 선언, 반환형 정보에 더해서, 함수 내에서 전달될 수 있는 예외의 종류(예외 데이터의 자료형)과 그 상황도 알아야 한다. 

그래야 해당 함수의 호출문장을 감싸는 적절한 try~catch 블록을 구성할 수 있다.



"즉, 함수를 정의할 때에는 함수 내에서 발생 가능한 예외의 종류를 명시해 주는 것이 좋다."



throw(int, char) 선언은 함수 내에서 예외상황의 발생으로 인해, int형 예외 데이터와 char형 예외 데이터가 전달될 수 있음을 알리는 것이다.

물론 위와 같이 함수가 선언되면, 함수로부터 int형 예외 데이터와 char형 예외 데이터만이 전달되어야 하며, 다른 자료형의 예외 데이터가 전달될 경우 terminate 함수의 호출로 인해서 프로그램은 종료되고 만다.






전달되는 예외의 자료형을 명시하는 부분이 비어있다. 

즉, 이는 어떠한 예외도 전달하지 않음을 의미한다. 따라서 위의 함수가 예외를 전달할 경우 프로그램은 그냥 종료가 된다.



※ unexpected 함수

- 함수의 선언에 명시되지 않은 예외가 전달될 경우 unexpected라는 이름의 함수가 호출이 되며, 이 함수의 기본 기능은 '프로그램의 종료(terminate 함수의 호출)'이다. 때문에 명시되지 않은 예외가 전달될 경우 프로그램이 종료되는 것이다.

Posted by scii
: