Point<int>, Point<double> 과 같은 템플릿 클래스의 자료형을 대상으로도 템플릿이 아닌 일반함수의 정의가 가능하고, 클래스 템플릿 내에서 이러한 함수를 대상으로 friend 선언도 가능하다.




24행: 이 함수는 템플릿 클래스인 Point<int> 의 객체 둘을 인자로 받고 반환하는 일반 함수이다.

Posted by scii
:

Template 2

Programming/C++ 2012. 10. 14. 20:17 |

※ C++ 표준 라이브러리는 템플릿을 기반으로 디자인된다. 따라서 템플릿을 잘 알면, 그 만큼 라이브러리에 대한 이해도와 활용능력이 향상된다.


클래스 템플릿을 기반으로 Point<int> 템플릿 클래스의 객체를 저장할 수 있는 객체는 어떻게 생성해야 할까?


BoundCheckArray<int> iarr(50);

-> 다음과 같이 객체를 생성하면, int형 데이터의 저장이 가능한 것처럼, 저장대상의 자료형이 Point<int>이니, 다음과 같이 객체를 생성하면 된다.


BoundCheckArray<Point<int>> oarr(50);


그리고 저장대상이 Point<int>형이 포인터라면,


BoundCheckArray<Point<int>*> oparr(50); 이렇게 선언하면 된다.


또한, 위의 문장은 typedef 선언을 통해 다음과 같이 구성해서 된다.


typedef Point<int>*    POINT_PTR;

BoundCheckArray<POINT_PTR> oparr(50);







템플릿 클래스라 하여 일반 클래스와 비교해서 다른 문법체계를 지니는 것은 아니다. 

우리가 알고 있는 C++ 문법의 이해를 기반으로 템플릿과 관련된 다양한 확장이 가능하다.

Posted by scii
:

클래스 템플릿도 멤버함수를 클래스 외부에 정의하는 것이 가능하다.


외부에 정의할 때 클래스이름<T>가 의미하는 바는 다음과 같다.

- "T에 대해 템플릿화 된 (클래스이름) 클래스 템플릿이다."

즉, 이것은 일반 클래스가 아니고 클래스 템플릿이라는 뜻으로 생각하면 된다.


또한 클래스 템플릿의 정의와 함수의 정의가 완전히 별개이기 때문에 각각에 대해서 문자 T가 무엇을 의미하는지 설명해야 한다.


# 일반적인 클래스의 정의아 마찬가지로 매개변수의 디폴트 값은 클래스 템플릿 내에만 표시한다.


★ 템플릿 클래스는 일반적인 클래스를 대상으로 해 왔던 동일한 방식으로 파일을 나누고 컴파일을 해보면 문제가 발생한다.!!!




파일을 나눌 때의 고려할 사


※ 컴파일은 파일단위로 이루어진다.


main 함수가 정의된 소스파일이 컴파일 될 때, 컴파일러는 총 3개의 템플릿 클래스를 생성해야 한다. 그리고 이를 위해서는 클래스 템플릿인 Point의 모든 것을 알아야 한다.

즉, 파일러는 헤더파일에 담긴 정보뿐만 아니라, 소스파일에 담긴 정보를 참조해야만 한다. (소스파일에 정의되어있는 기능을 알아야 템플릿 클래스를 생성할 수 있기때문에)

그래서 일반적으로 파일을 나누면 참조할 만한 것이 없기 때문에 문제가 발생한다. 




결법


- 헤더파일에 템플릿의 모든 정보를 모두 넣는다. 

- #include 문으로 소스파일도 포함시킨다. ex) #include "gg.cpp"


템플릿은 이러한 방법을 사용해서 템플릿의 모든 정보를 소스파일에 전달해야만 한다. 






배열 클래스의 템플릿화


여기선 헤더파일 안에 클래스 템플릿 BoundCheckArray의 모든 것을 담아두었다. 따라서 이 헤더파일 하나만 포함을 하면, BoundCheckArray 템플릿 기반의 객체를 생성할 수 있다.

/* gg.h */

#ifndef     __GG_H__

#define     __GG_H__


#include <iostream>

#include <cstdlib>

using namespace std;


template <typename T>

class BoundCheckArray

{

    private:

        T * arr;

        int arrLen;


        BoundCheckArray(const BoundCheckArray& ref){    }

        BoundCheckArray& operator=(const BoundCheckArray& ref){return *this;}


    public:

        BoundCheckArray(int len);

        T& operator[](int idx);

        T operator[](int idx) const;

        int GetArrLen() const;

        ~BoundCheckArray();

};


template <class T>

BoundCheckArray<T>::BoundCheckArray(int len)

    :arrLen(len)

{

    arr = new T[len];

}


template <class T>

T& BoundCheckArray<T>::operator[](int idx)

{

    if(idx < 0 || idx >= arrLen)

    {

        cout<<"Array index out of bound exception"<<endl;

        exit(1);

    }

    return arr[idx];

}


template <class T>

T BoundCheckArray<T>::operator[](int idx) const

{

    if(idx < 0 || idx >= arrLen)

    {

        cout<<"Array index out of bound exception"<<endl;

        exit(1);

    }

    return arr[idx];

}


template <class T>

int BoundCheckArray<T>::GetArrLen() const

{

    return arrLen;

}


template <class T>

BoundCheckArray<T>::~BoundCheckArray()

{

    delete []arr;

}


#endif


/* point.h */


/* point.cpp */


/* main.cpp */


Posted by scii
:

함수를 템플릿으로 정의했듯이 클래스도 템플릿으로 정의가 가능하다. 

그리고 이렇게 정의된 템플릿을 가리켜 '클래스 템플릿(Class Template) 이라 하며, 이를 기반으로 컴파일러가 만들어 내는 클래스를 가리켜 '템플릿 클래스(Template Class)' 라 한다.


클래스 템플릿의 정의방법은 함수 템플릿의 정의방법가 동일하다.



23행: 정의된 템플릿을 기반으로 컴파일러가 만들어 내는 템플릿 클래스 Point<int> 의 객체 생성을 명령하고 있다. 여기서 <int> 는 T를 int로 하여 만든 템플릿 클래스를 의미한다.


컴파일러는 '클래스 템플릿' 을 기반으로 '템플릿 클래스' 를 만들어 낸다. 위 예제의 경우 총 3개의 템플릿 클래스가 만들어지며, 이들 각각은 다음과 같이 표현을 해서 일반 클래스와 구분을 짓는다.


Point<int> 템플릿 클래스

Point<double> 템플릿 클래스

Point<char> 템플릿 클래스


※ 템플릿 함수를 호출할 때와 마찬가지로 템플릿 클래스의 객체를 생성할 때에도 <int>, <double> 등등과 같은 자료형 정보를 생략할 수 있을까?

- 이 경우에는 생략이 불가능하다. 클래스 템플릿 기반의 객체생성에는 반드시 자료형 정보를 명시하도록 되어있다.

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

Template 2  (0) 2012.10.14
클래스 템플릿의 선언과 정의의 분리  (1) 2012.10.11
함수 템플릿의 특수화(Specialization)  (0) 2012.10.10
Template  (0) 2012.10.10
string 클래스의 디자인  (0) 2012.10.09
Posted by scii
:


16행: 여기서는 문자열을 대상으로 호출하고 있다. 하지만 그 결과에 대해서는 아무런 의미도 부여할 수 없게 된다. (단순히 주소 값의 비교결과가 반환되므로)


만약에 문자열의 길이비교가 목적이라면 다음의 형태로 템플릿 함수가 구성되어야 의미가 있다.

const char* Max(const char* a, const char* b)

{

return strlen(a) > strlen(b) ? a : b;

}


만약, 사전편찬 순서의 비교가 목적이라면...

const char* Max(const char* a, const char* b)

{

return strcmp(a, b) > 0 ? a : b;

}


이렇듯 상황에 따라서 템플릿 함수의 구성방법에 예외를 둘 필요가 있는데, 이 때 사용되는 것이 "함수 템플릿의 특수화(Specialization of Function Template)" 이다.




11~16행: 함수 템플릿 Max를 char* 형에 대해서 특수화 하였다.

18~23행: 함수 템플릿 Max를 const char* 형에 대해서 특수화 하였다.


위 예제의 다음 정의는,


template <>

char*~~~

{

...

}


컴파일러에게 다음의 메시지를 전달하는 것이다.


"char* 형 함수는 내가 이렇게 제시를 하니, char* 형 템플릿 함수가 필요한 경우에는 별도로 만들지말고 이것을 써라."



그리고 이 두 '함수 템플릿의 특수화' 정의는 특수화하는 자료형의 정보 <char*>, <const char*> 를 생략한 형태이며, 이를 생햑하지 않고 정의를 하면 다음의 형태가 된다.





특수화하는 자료형 정보를 생략하건 생략하지 않건 그 의미하는 바에 차이가 없으나, 가급적이면 자료형 정보를 명시하는 것이 뜻을 명확히 하는 방법이 된다.


※ 원래는 저렇게 자료형 정보를 표시해야만 했었다.!! 그러므로 저렇게 표시를 하자!! 저렇게 표시만해도 일반 함수인지 아니면 템플릿으로 만들어진 함수인지를 금새 구분하니까~~~!!!

Posted by scii
:

Template

Programming/C++ 2012. 10. 10. 00:20 |

함수 템플릿

- 함수 템플릿은 함수를 만들어 낸다. 함수의 기능은 결정되어 있지만, 자료형은 결정되어 있지 않아서 결정해야 한다.


함수 템플릿 이라는 것은 함수를 만드는 도구가 된다. 모형자가 모형을 만드는 도구가 되는 것처럼!! 그리고 모형자가 다양한 색의 모형을 만들어 내는 것처럼, 함수 템플릿도 다양한 자료형의 함수를 만들어 낼 수 있다.


int Add(int a, int b)

{

return a+b;

}


위에 정의한 함수의 자료형을 T로 대신할 수 있다. 


T Add(T a, T b)

{

return a+b;

}


int 형 자료형을 T로 대신했음을 알 수 있는데, 이는 자료형을 결정짓지 않은, 그래서 나중에 T를 대신해서 실제 자료형을 결정하겠다는 뜻이다.


그런데 이것이 전부가 아니다!! 컴파일러에게 다음과 같은 메시지를 전달해야 한다.


"T는 자료형을 결정짓지 않겠다는 의미로 사용한 것이다. 즉, 함수를 만들어 내는 템플릿을 정의하기 위해서 사용된 것이다."

이러한 메시지를 담아야만 함수 템플릿이 완성된다.


template <typename T>

T라는 이름을 이용해서 아래의 함수를 템플릿으로 정의한다는 의미이다.


※ typename 을 대신해서 class 를 사용할 수도 있다.

template <typename T> 선언을 대신해서 template <class T> 를 선언해도 똑같은 의미이다.

그리고, T라는 문자대신 다른 문자를 사용해도 된다.



14행에 Add<int>(15, 20); 라는 구문이 등장한다. 여기서 <int>가 의미하는 바는 "T를 int로 해서 만들어진 Add함수를 호출하라" 라는 의미가 된다.


위 예제에서 함수 템플릿을 기반으로 만들어진 함수는 이렇게 된다.


int Add<int>(int a, int b)

{

return a+b;

}


double Add<double>(double a, double b)

{

return a+b;

}


※ 함수 템플릿으로 한번 함수가 만들어지면, 그 다음에는 만들어진 함수를 호출할 뿐 새로 함수를 만들지는 않는다.

즉, 함수는 자료형당 하나씩만 만들어진다.


14행에서 주석이 설명하는 것처럼 함수 호출하듯이 Add(4, 3) 이렇게 써도 된다. 

왜냐하면, 전달하는 인자의 자료형을 참조하여 호출될 함수의 유형을 컴파일러가 결정하기 때문이다.


함수 템플릿 & 템플릿 함수


# 함수 템플릿


template <typename T>

T Add(T a, T b)

{

return a+b;

}


이것을 가리켜 "함수 템플릿(Function Template)" 이라 한다.

즉, 호출이 가능한 함수가 아닌, 템플릿임을 강조한 것이다.


함수 템플릿 => 함수를 만드는데 사용되는 템플릿




# 템플릿 함수


int Add<int>(int a, int b)

{

return a+b;

}


위의 템플릿을 기반으로 컴파일러가 만들어 내는 이런 유형의 함수들을 가리켜 "템플릿 함수(Template Function)" 이라 한다.

즉, 템플릿을 기반으로 만들어진, 호출이 가능한 함수임을 강조한 것이다.


템플릿 함수 => 템플릿을 기반으로 만들어진 함수


※ 템플릿 함수의 또 다른 표현

- 템플릿 함수는 컴파일러에 의해 성성된 함수이기 때문에 "생성된 함수(Generated Function)" 으로도 불린다.     

템플릿 클래스 역시 "생성된 클래스(Generated Class)" 라고도 불린다.





둘 이상의 형(type)에 대해 템플릿 선언하기


템플릿의 정의에도 다양한 자료형의 선언이 가능할 뿐만 아니라, 둘 이상의 형(type)에 대해서 템플릿을 선언할 수도 있다.



5행: 함수 템플릿의 매개변수 조차도 기본 지료형으로 선언될 수 있다.

7행: 인자로 전달된 num의 값을 T1과 T2로 명시되는 자료형으로 형 변환해서 출력하고 있다.

13~17행: 위의 함수 템플릿은 매개변수 형이 double로 선언되었기 때문에 전달되는 인자를 통해서는 T1과 T2의 자료형을 결정짓지 못한다. 따라서 이러한 경우에는 템플릿 함수의 호출형식을 완전히 갖춰서 호출해야 한다.


※ C++ 에서는 데이터에 소괄호를 묶는 형태로 형 변환을 명령할 수 있다.

ex) int num = (int)3.14;        =>        int num = int(3.14); 

이 두개의 문장은 완전히 일치한다.




template의 다른 예)






Posted by scii
:

C++ 표준 라이브러리에는 string 이라는 이름의 클래스가 정의되어 있다. 클래스의 이름이 의미하는 것처럼, 문자열의 처리를 목적으로 정의된 클래스이며, 이 클래스의 사용을 위해서는 헤더파일 <string> 을 포함해야 한다.


string 클래스를 대체할 수 있는 String 클래스를 구현해 보자~!!


표준 string 클래스의 분석


1. 문자열을 인자로 전달받는 생성자의 정의


2. 생성자, 소멸자, 복사 생성자 그리고 대입 연산자의 정의

- 저장하고자 하는 문자열의 길이가 일정치 않다. 따라서 문자열의 저장을 위한 메모리 공간을 생성자 내에서 동적 할당해야 한다. 

그리고 이로 인해서 소멸자를 정의해야 하며, 깊은 복사를 하는 복사 생성자와 대입 연산자까지 함께 정의하고자 한다.


3. 결합된 문자열로 초기화된 객체를 반환하는 + 연산자의 오버로딩

- + 연산자의 반환 값은 2가지로 생각할 수 있다.

1. 문자열의 주소 값(str1과 str2가 지니고 있는 문자열을 합한 문자열의 주소 값)

2. string 객체(str1과 str2가 지니고 있는 문자열을 합한 문자열을 저장하고 있는 객체)


4. 문자열을 덧붙이는 += 연산자의 오버로딩


5. 내용비교를 진행하는 == 연산자의 오버로딩


6. 콘솔입출력이 가능하도록 <<, >> 연산자의 오버로딩





61행: 주석에 달린 것 처럼 *this = *this+s;

                return *this; 

이렇게 정의해도 똑같다. 이러한 형태의 정의는 간결해 보이고 이해하기도 좋지만, 덧셈의 과정에서 객체가 추가로 생성된다는 단점이 있다.

하지만 컴퓨팅 파워가 좋은 환경이라면 이 정도는 단점이 될 수 없다. 이러한 형태의 구현도 생각해볼 만하다.


※ #include <string> 헤더파일을 포함시키면, 이렇게 직접 구현하지않고 이런식이 되도록 쓸 수 있다.


Posted by scii
:

두 객체의 자료형이 일치할 때에만 대입연산이 가능하다.





32행을 조금 풀어서 쓰면 다음과 같다.


num = Number(30);                            // 1단계. 임시객체의 생성

num.operator=(Number(30));            // 2단계. 임시객체를 대상으로 하는 대입 연산자의 호출


여기서의 핵심은 임시객체의 생성이다. 그리고 이러한 임시객체의 생성을 통해서 대입연산이 진행되는 데에는 다음과 같은 문법적 기준이 존재한다.


"A형 객체가 와야 할 위치에 B형 데이터(또는 객체)가 왔을 경우, B형 데이터를 인자로 전달받는 A형 클래스의 생성자 호출을 통해서 A형 임시객체를 생성한다."


때문에 위의 예제에서는 'Number형 객체가 와야 할 위치에 int 형 데이터가 와서, int 형 데이터를 인자로 전달받는 Number 클래스의 생성자 호출을 통해서 Number 형 임시객체를 생성한 것' 이다.


이렇듯, 기본 자료형 데이터를 객체로 형 변환하는 것은 적절한 생성자의 정의를 통해서 얼마든지 가능하다. (반대로 객체를 기본 자료형 데이터로 형 변환하는 것도 가능하다)






앞서 정의한 Number 클래스를 대상으로 덧셈연산이 가능하게끔 하는 예제




위 예제에서 정의한 형 변환 연산자는 다음과 같다.


operator int()

{

return num;

}


이렇듯 형 변환 연산자는 반환형을 명시하지 않는다. 하지만 return 문에 의한 값의 반환은 얼마든지 가능하다.

그리고 오버로딩 된 연산자의 이름이 operator+ 이면, + 연산자가 등장했을 때 호출되는 것과 유사하게 operator int 는 다음의 의미로 이해하면 된다.


int 형으로 형 변환해야 하는 상황에서 호출되는 함수이다.


※ int 형 말고도 다른 자료형의 이름이 올 수 있다.


즉, int 형으로 형 변환되어야 하는 상황에서 호출이 되며, 이 때 return 문에 의해 반환되는 값이 int 형으로의 형 변환 결과가 되는 것이다.


Number num2 = num1+20;


num1 객체의 operator int 함수가 호출되어, 이 때 반환되는 값 30과 20의 덧셈연산이 진행되며, 이 연산의 결과로 num2 객체가 생성된 것이다.

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

Template  (0) 2012.10.10
string 클래스의 디자인  (0) 2012.10.09
()연산자 오버로딩과 펑터(Functor)  (0) 2012.10.07
스마트 포인터  (0) 2012.10.04
포인터 연산자 오버로딩  (0) 2012.10.04
Posted by scii
: