Project Hub

14. template 본문

C++/c++ basic

14. template

safy 2022. 12. 21. 14:18
728x90
반응형

이전 글

2022.12.21 - [C++/c++ basic] - 13. 파일 입출력

 

13. 파일 입출력

이전 글 2022.12.21 - [C++/c++ basic] - 12. 입출력 (istream, ostream) 12. 입출력 (istream, ostream) 이전 글 2022.12.21 - [C++/c++ basic] - 11. virtual 함수와 다형성 11. virtual 함수와 다형성 이전 글 2022.12.21 - [C++/c++ basic] -

projecthub.tistory.com


C++ 템플릿 (template)

 

아래는 템플릿 형식으로 만든 vector 클래스다.

실제 사용 시, CVector<자료형> test_vector; 형식 들으로 사용이 되는데,

이러한 것을 클래스 템플릿 인스턴스화 라고 한다.

#pragma once
 
#include <iostream>
 
template <typename T>
class CVector
{
public:
    CVector(int n = 1) : m_pData(new T[n]), m_iCapacity(n), m_iLength(0) {};
    ~CVector() { if (m_pData) delete[] m_pData; }
 
public:
    void push_back(T s)
    {
        if (m_iLength >= m_iCapacity)
        {
            T* pTemp = new T[m_iCapacity * 2];
            if (NULL != pTemp)
            {
                for (int i = 0; i < m_iLength; i++)
                    pTemp[i] = m_pData[i];
            }
 
            delete[] m_pData;
            m_pData = pTemp;
            m_iCapacity *= 2;
        }
 
        m_pData[m_iLength] = s;
        m_iLength++;
    }
 
    void remove(int iIdx)
    {
        for (int i = iIdx + 1; i < m_iLength; i++)
            m_pData[i - 1] = m_pData[i];
    }
    int size()
    {
        return m_iLength;
    }
 
    T operator[](int iIdx) { return m_pData[iIdx]; }
 
public:
    T*                  m_pData;
    int                 m_iCapacity;
    int                 m_iLength;
};


템플릿 특수화 (template specialization)

아래와 같은 클래스 템플릿이 정의되어있을 때,

template<typename A, typename B, typename C>
class test {};

일부 겅우에 대해서 따로 처리를 할 수 있다.

template <typename B>
class test<int, B, double> {};
 
template <>
class test<int, int, double> {};


중요한 점은, 전달하는 템플릿 인자가 없더라도 특수화 하고 싶다면, template<> 라도 남겨줘야 한다는 점이다.

아래는 템플릿 특수화로 bool vector 클래스를 구현한 것이다.

CVector 클래스 호출 시, CVector<bool> 로 지정할 경우, 아래 클래스가 호출된다.

기존 vector 클래스
...
 
// 템플릿 특수화
template <>
class CVector<bool>
{
public:
    CVector(int n = 1) : m_puiData(new unsigned int[n / 32 + 1]), m_iCapacity(n / 32 + 1), m_iLength(0) { for (int i = 0; i < m_iCapacity; i++) m_puiData[i] = 0; };
    ~CVector() { if (m_puiData) delete[] m_puiData; }
 
public:
    void push_back(bool s)
    {
        if (m_iLength >= m_iCapacity * 32)
        {
            unsigned int* puiTemp = new unsigned int[m_iCapacity * 2];
            if (NULL != puiTemp)
            {
                for (int i = 0; i < m_iCapacity; i++)
                    puiTemp[i] = m_puiData[i];
 
                for (int i = m_iCapacity; i < m_iCapacity * 2; i++)
                    puiTemp[i] = 0;
            }
 
            delete[] m_puiData;
            m_puiData = puiTemp;
            m_iCapacity *= 2;
        }
 
        if (s)
            m_puiData[m_iLength / 32] |= (1 << (m_iLength % 32));
 
        m_iLength++;
    }
 
    void remove(int iIdx)
    {
        for (int i = iIdx + 1; i < m_iLength; i++)
        {
            if (m_puiData[i / 32] & (1 << i % 32))
            {
                m_puiData[(i - 1) / 32] |= (1 << (i - 1) % 32);
            }
            else
            {
                unsigned int all_ones_except_prev = 0xFFFFFFFF; // = 1111 1111 1111 1111 1111 1111 1111 1111
                all_ones_except_prev ^= (1 << ((i - 1) % 32)); // ^= 연산은 다를 경우 1 반환
                m_puiData[(i - 1) / 32] &= all_ones_except_prev;
            }
                 
        }
 
        m_iLength--;
    }
 
    int size()
    {
        return m_iLength;
    }
    bool operator[](int iIdx) { return (m_puiData[iIdx / 32] & (1 << (iIdx % 32))) != 0; }
 
public:
    unsigned int*       m_puiData;
    int                 m_iCapacity;
    int                 m_iLength;
};


함수 객체 (Function Object - Functor)

  • 함수는 아니지만 함수인 척을 하는 객체

 

아래는 함수 객체에 대한 예시다.


추가로 struct Com1, Com2 로 선언을 한 부분이 있다.

c++ 에서 class 와 struct 는 똑같은 키워드다.

차이점은 struct는 디폴트로 public 이고, class는 디폴트로 private 라는 것이다.

따라서 struct 로 선언해서 public: 한줄 쓰는 것을 줄일 수 있다.

template <typename T>
class CVector
{
public:
    CVector(int n = 1) : m_pData(new T[n]), m_iCapacity(n), m_iLength(0) {};
    ~CVector() { if (m_pData) delete[] m_pData; }
 
public:
    void push_back(T s)
    {
        if (m_iLength >= m_iCapacity)
        {
            T* pTemp = new T[m_iCapacity * 2];
            if (NULL != pTemp)
            {
                for (int i = 0; i < m_iLength; i++)
                    pTemp[i] = m_pData[i];
            }
 
            delete[] m_pData;
            m_pData = pTemp;
            m_iCapacity *= 2;
        }
 
        m_pData[m_iLength] = s;
        m_iLength++;
    }
 
    void remove(int iIdx)
    {
        for (int i = iIdx + 1; i < m_iLength; i++)
            m_pData[i - 1] = m_pData[i];
    }
    int size()
    {
        return m_iLength;
    }
 
    void swap(int iFirst, int iSecond)
    {
        T temp = m_pData[iFirst];
        m_pData[iFirst] = m_pData[iSecond];
        m_pData[iSecond] = temp;
    }
 
    T operator[](int iIdx) { return m_pData[iIdx]; }
 
public:
    T*                  m_pData;
    int                 m_iCapacity;
    int                 m_iLength;
};
 
// 함수 객체 예시
struct Comp1
{
    bool operator()(int a, int b) { return a > b; }
};
 
struct Comp2
{
    bool operator()(int a, int b) { return a < b; }
};
 
template <typename Cont, typename Comp>
void bubble_sort(Cont& cont, Comp& comp)
{
    for (int i = 0; i < cont.size(); i++)
    {
        for (int j = i + 1; j < cont.size(); j++)
        {
            if (!comp(cont[i], cont[j]))        // comp 라는 객체가 마치 함수처럼 사용되고 있다.
                cont.swap(i, j);
        }
    }
}

 

 

타입이 아닌 템플릿 인자

  • 정수 타입들 (bool, char, int, long 등)
  • 포인터 타입
  • enum 타입
  • std::nullptr_t (널 포인터)
template <typename T, int num> // int 형의 템플릿 인자

T add_num(T t)
{
    return t + num;
}
디폴트 템플릿 인자
template <typename T>
struct Compare
{
    bool operator()(const T& a, const T& b) { return a < b; }
};
 
template <typename T, typename Comp = Compare<T>> // 디폴트 템플릿 인자
T Min(T a, T b)
{
    Comp comp;
    if (comp(a, b))                             // 함수처럼 사용
        return a;
    else
        return b;
}
728x90
반응형
Comments