Home [C++ 프로그래밍] 13강 - 템플릿
Post
Cancel

[C++ 프로그래밍] 13강 - 템플릿

💡해당 게시글은 방송통신대학교 이병래 교수님의 'C++ 프로그래밍' 강의를 개인 공부 목적으로 메모하였습니다.



학습 개요


  • 컨테이너 클래스는 객체들을 저장하는 클래스
  • 만일 여러 가지 클래스의 객체에 대해 각각 컨테이너 클래스가 필요하다면 여러 개의 컨테이너 클래스가 선언되어야 할 것임
  • 이 경우 동일한 코드를 반복적으로 작성해야 하는 번거로움이 따르는데 템플릿은 이러한 경우 유용하게 활용할 수 있는 기능임
  • 클래스 템플릿을 선언해 놓으면 그 안에서 대상으로 하는 객체의 클래스 등을 인수로 전달하여 필요한 클래스가 선언된 효과를 얻게 됨
  • 클래스 템플릿을 선언하고 활용하는 방법에 대하여 학습함
  • 클래스 및 클래스의 멤버 함수 뿐만 아니라 일반 함수도 템플릿으로 선언함으로써, 여러 가지 대상에 맞는 함수가 선언된 효과를 얻을 수 있음
  • 함수 템플릿을 선언하여 활용하는 방법에 대하여 학습함



학습 목표


  • 컨테이너 클래스 및 템플릿의 개념을 설명할 수 있음
  • 클래스 템플릿을 선언하고, 이를 이용하여 객체를 정의할 수 있음
  • 함수 템플릿을 선언할 수 있음



주요 용어


  • 컨테이너 클래스(container class)
    • 객체를 저장하는 클래스
  • 클래스 템플릿(class template)
    • 클래스를 선언하기 위한 형판으로, 전달된 템플릿 인수에 따라 이에 맞는 클래스가 선언됨
  • 일반화 프로그래밍(generic programming)
    • 클래스, 함수 등을 대상 자료형 등을 특정하지 않은 상태로 선언한 후, 필요할 때 자료형 등을 파라미터를 통해 전달하여 정의하는 프로그래밍 방식
  • 정렬(sorting)
    • 데이터를 정해진 기준에 맞는 순서로 다시 배열하는 것



강의록


컨테이너 클래스와 템플릿

컨테이너 클래스

  • 컨테이너 클래스(container class)란?
    • 객체를 저장하는 클래스

      image.png

  • int형 데이터를 저장하는 스택 클래스 - Stack1.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      typedef int STACK_ITEM;
      class Stack {
      public:
        enum { MAXSTACK=20 };
        int top;
        STACK_ITEM item[MAXSTACK];
      public:
        Stack(); // 생성자
        bool empty() const;
        bool full() const;
        void initialize();
        void push(STACK_ITEM s);
        STACK_ITEM pop();
      };
    
  • float형 데이터를 저장하는 스택 클래스

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      typedef float STACK_ITEM;
      class Stack {
      public:
        enum { MAXSTACK=20 };
        int top;
        STACK_ITEM item[MAXSTACK];
      public:
        Stack(); // 생성자
        bool empty() const;
        bool full() const;
        void initialize();
        void push(STACK_ITEM s);
        STACK_ITEM pop();
      };
    
  • int형 스택과 float형 스택이 모두 필요할 경우

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
      typedef int INT_ITEM;
      class StackInt {
        
        INT_ITEM item[MAXSTACK];
        
      };
        
      void StackInt::push(INT_ITEM n) {
        item[--top] = n;
      }
        
      typedef float FLOAT_ITEM;
      class StackFloat {
        
        FLOAT_ITEM item[MAXSTACK];
        // StackInt와 동일한 코드
        // 단 INT_ITEM만 FLOAT_ITEM으로 변경
        
      };
        
      void StackFloat::push(FLOAT_ITEM n) {
        item[--top] = n;
      }
    
    • 동일 코드 반복 발생

템플릿

  • 템플릿(template)이란?
    • 클래스, 함수 등을 선언하기 위한 형판
    • 특정 자료형이 아닌 일반 자료형을 대상으로 템플릿을 선언함
      • 자료형, 상수 등을 매개 변수를 통해 템플릿에 전달하면 이에 따라 클래스나 함수가 자동으로 선언됨
      • 일반화 프로그래밍(generic programming)
    • 여러 가지 대상을 위한 클래스나 함수를 템플릿으로 선언함으로써, 동일한 코드를 반복적으로 작성하는 것을 방지함

클래스 템플릿

클래스 템플릿의 선언

  • 클래스 템플릿의 선언 형식

    1
    2
    3
    4
    
      template <templateParameters>
      class ClassTemplateName {
        
      };
    
    • templateParameters
      • 템플릿 매개 변수 목록
      • 자료형을 받을 템플릿 매개 변수
        • typename T또는 class T로 표기 (T는 사용자 정의 명칭)
    • ClassTemplateName
      • 클래스 템플릿 이름
  • 예: Stack 클래스 템플릿 - StackT.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      template <typename T>
      class Stack {
      public:
        T *buf; // buffer pointer
        int top; // stack top
        int size; // 스택의 크기
      public:
        Stack(int s); // 생성자
        virtual ~Stack(); // 소멸자
        bool full() const;
        bool empty() const;
        void push(const T& a);
        void push(T&& a);
        T&& pop();
      };
    

클래스 템플릿의 멤버 함수 선언

  • 템플릿 선언문 외부에서 멤버 함수를 선언하는 형식

    1
    2
    3
    4
    5
    
      template <templateParameters>
      ReturnType ClassTemplateName<args>::funcName(fParameterList)
      {
        
      }
    
    • ReturnType
      • 멤버 함수의 반환 자료형
    • funcName
      • 멤버 함수의 이름
    • args
      • templateParameters의 매개 변수
    • fParameterList
      • 멤버 함수의 형식 매개 변수 목록
  • 예: Stack 클래스 템플릿 - StackT.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
      template <typename T> Stack<T>::Stack(int s) : size(s), top(0)
      {
        buf = new T[s];
      }
        
      template <typename T> Stack<T>::~Stack() {
        delete[] buf;
      }
        
      template <typename T> bool Stack<T>::full() const
      {
        return top == size;
      }
        
      template <typename T> bool Stack<T>::empty() const
      {
        return top == 0;
      }
        
      template <typename T> void Stack<T>::push(const T& a)
      {
        buf[top++] = a;
      }
        
      template <typename T> void Stack<T>::push(T&& a)
      {
        buf[top++] = move(a);
      }
        
      template <typename T> T&& Stack<T>::pop()
      {
        return move(buf[--top]);
      }
    

클래스 템플릿의 객체 정의

  • 클래스 템플릿의 객체 정의의 형식

    1
    
      ClassTemplateName<ClassName> objName(constrArgs);
    
    • ClassName
      • 템플릿 매개 변수에 전달할 클래스 또는 자료형 이름 등의 템플릿 인수
    • objName
      • 정의할 객체의 이름
    • constrArgs
      • 생성자에 전달할 인수

클래스 템플릿의 객체 사용

  • 예: char을 저장하는 스택의 활용 - StackMain.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      #include "StackT.h"
      #include "MyString.h"
      using namespace std;
        
      int main()
      {
        Stack<char> sc(100); // char 스택 선언
        sc.push('a'); // char 스택 사용
        sc.push('b');
        cout << "CHAR STACK : ";
        while (!sc.empty())
        {
            cout << sc.pop();
        }
        cout << endl;
        
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      template <typename T>
      class Stack {
      public:
        T *buf; // buffer pointer
        int top; // stack top
        int size; // 스택의 크기
      public:
        Stack(int s); // 생성자
        virtual ~Stack(); // 소멸자
        bool full() const;
        bool empty() const;
        void push(const T& a);
        void push(T&& a);
        T&& pop();
      };
    
  • 예: int를 저장하는 스택의 활용 - StackMain.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      int main()
      {
        
        Stack<int> si(50);  // int 스택 선언
        si.push(5); // int 스택 사용
        si.push(10);
        cout << "INT STACK : ";
        while (!si.empty())
        {
            cout << si.pop();
        }
        cout << endl;
          
        Stack<MyString> msStack(10); // MyString 스택 선언
        MyString s1("KNOU");
        
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      template <typename T>
      class Stack {
      public:
        T *buf; // buffer pointer
        int top; // stack top
        int size; // 스택의 크기
      public:
        Stack(int s); // 생성자
        virtual ~Stack(); // 소멸자
        bool full() const;
        bool empty() const;
        void push(const T& a);
        void push(T&& a);
        T&& pop();
      };
    
  • 예: MyString을 저장하는 스택의 활용 - StackMain.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      int main()
      {
        
        Stack<MyString> msStack(10); // MyString 스택 선언
        MyString s1("KNOU");
        MyString s2("Dep.");
        MyString s3("CS");
        msStack.push(s1); // MyString 스택 사용
        msStack.push(s2 + s3);
        cout << "MYSTRING STACK : ";
        while (!msStack.empty())
        {
            cout << msStack.pop() << " ";
        }
        cout << endl;
        return 0;
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      template <typename T>
      class Stack {
      public:
        T *buf; // buffer pointer
        int top; // stack top
        int size; // 스택의 크기
      public:
        Stack(int s); // 생성자
        virtual ~Stack(); // 소멸자
        bool full() const;
        bool empty() const;
        void push(const T& a);
        void push(T&& a);
        T&& pop();
      };
    

클래스 템플릿의 대상 객체 관련 유의 사항

  • 사용자가 선언한 클래스의 객체를 저장하는 컨테이너
    • 기본 자료형 외에도 사용자 정의 클래스 객체를 저장하기 위해 컨테이너 클래스 템플릿을 사용할 수 있음
      • ex) MyString 객체를 저장하기 위한 스택
    • 이 경우 클래스 템플릿에서 필요로 하는 멤버 함수가 대상 클래스에 포함되어 있어야 함
      • 클래스 템플릿 Stack에 저장할 객체에는 디폴트 생성자, 대입 연산자, 이동 대입 연산자 등이 필요함
    • ex)

      1
      2
      3
      4
      5
      6
      7
      8
      
        class Person {
        	string name;
        public:
          Person(const string& n) : name(n) {}
          void print() {
              cout << name;
          }
        };
      
      1
      2
      3
      4
      5
      
              
        int main() {
          Stack<Person> pStack(10); // 에러 발생
              
        }
      
      1
      2
      3
      4
      5
      
              
        template <typename T>
        Stack<T>::Stack(int s) : size(s), top(s) {
          buf = new T[s]; // Person에 디폴트 생성자가 필요함
        }
      

비 자료형 템플릿 매개 변수

  • 템플릿 매개 변수를 통해 전달할 수 있는 인자
    • 자료형 매개 변수
      • 기본 자료형, 클래스, 구조체 등
    • 비 자료형 매개 변수
      • 정수형 자료형의 상수식
      • 객체나 함수에 대한 포인터
      • 객체나 함수에 대한 l-value 참조
      • 멤버에 대한 포인터
  • 비자료형 매개 변수의 사용 예

    1
    2
    3
    
      template <typename T, int size> class Buffer {
        T buf[size];
      };
    
    1
    2
    3
    4
    5
    6
    7
    8
    
      void f()
      {
        Buffer<char, 128> buf1;     // 크기가 128개인 char형 버퍼 선언
        Buffer<Complex1, 20> buf2;  // 크기가 20개인 complex1 버퍼 선언
        int n = 10;
        Buffer<char, n> buf3;       // 에러! - 실행시 값이 결정되는 수식
            
      }
    

함수 템플릿

함수 템플릿의 선언

  • 함수 템플릿 선언 형식

    1
    2
    3
    4
    
      template <templateParameters>
      ReturnType funcName(fParameterList) {
        // 함수 몸체
      }
    
    • templateParameters
      • 템플릿 매개 변수 선언
    • funcName
      • 함수 템플릿 이름

함수 템플릿의 활용 예 - swapFT

  • 함수 템플릿 선언 - SwapFunc.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      #ifndef SWAP_FUNCTION_TEMPLATE_H_INCLUDED
      #define SWAP_FUNCTION_TEMPLATE_H_INCLUDED
        
      #include <utility> // for std::move
        
      template <typename ANY>
      void swapFT(ANY &a, ANY &b)
      {
        ANY temp = move(a); // 이동 대입 연산자가 필요함
        a = move(b);
        b = move(temp);
      }
        
      #endif
    
  • 함수 템플릿을 이용한 프로그램 - SwapFTMain.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
      #include <iostream>
      #include "SwapFT.h"
      #include "MyString.h"
      using namespace std;
        
      int main()
      {
        int x = 10, y = 20;
        cout << "x = " << x << ", y = " << y << endl;
        cout << "값 교환 후 -> ";
        swapFT(x, y);
        cout << "x = " << x << ", y = " << y << endl << endl;
        
        MyString s1("KNOU"), s2("CS");
        cout << "s1 = " << s1 << ", s2 = " << s2 << endl;
        swapFT(s1, s2);
        cout << "값 교환 후 -> ";
        cout << "s1 = " << s1 << ", s2 = " << s2 << endl;
        return 0;
      }
    

함수 템플릿의 활용 예 - sortFT

  • 버블 정렬
    • 배열의 선두에서 시작하여 차례로 인접한 두 값을 비교하여 앞의 값이 크면 값을 교환하는 것을 반복

      image.png

      image.png

      image.png

함수 템플릿의 활용 예 - sortFT

  • 함수 템플릿 선언 - SortFT.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
      #include "SwapFT.h"
        
      // bubble sort algorithm
      template <typename T> void sortFT(T arr[], int size)
      {
        bool doAgain = true;
        for (int i = 1; doAgain; i++) {
          doAgain = false;
          for (int j = 0; j < size - i; j++)
          {
            if (arr[j] > arr[j + 1])
            {
              swapFT(arr[j], arr[j + 1]);
              doAgain = true;
            }
          }
        }
      }
    
  • 함수 템플릿을 이용한 프로그램 - SortMain.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
      #include <iostream>
      #include "SortFT.h"
      #include "MyString.h"
      using namespace std;
        
      int main()
      {
        int x[10] = { 6, 0, 3, 1, 2, 9, 4, 5, 7, 8 };
        sortFT(x, 10);
        for (auto i : x)
          cout << i << " ";
        cout << endl;
        
      }
    



연습 문제


  1. 객체를 저장하기 위한 클래스를 나타내는 것은?

    a. 컨테이너 클래스

    • 컨테이너 클래스는 스택, 큐, 배열, 리스트 등 객체들을 저장하기 위한 클래스를 통칭하는 것임
  2. 지정된 개수의 객체를 저장할 수 있는 배열을 나타내는 컨테이너 클래스 템플릿을 선언하고자 한다. 위 지문 문장의 ㈀, ㈁, ㈂, ㈃에 넣을 적절한 내용은?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
     // (ㄱ) <typename (ㄴ)>
     class Array {
       T* buf;
       int size;
     public:
       Array(int s) : size(s) { buf = // (ㄷ); }
       ~Array() { delete [] buf; }
       // (ㄹ) operator [] (int i) { return buf[i]; }
     };
    

    a. ㈀ template, ㈁ T, ㈂ new T[s], ㈃ T&

    • ㈀에는 템플릿 선언을 위한 키워드인 template이 필요하며, ㈁에는 이 컨테이너 클래스가 저장하고자 하는 자료형에 해당되는 매개 변수가 필요함
    • 선언문의 내용으로 볼 때 buf의 자료형으로 사용된 T가 이에 해당됨을 알 수 있음
    • ㈂에는 T클래스의 객체를 동적 할당하는 명령이 필요하며, ㈃은 buf[i]T클래스 객체이므로 []연산자는 T의 참조를 반환하도록 함
  3. 위 지문과 같이 클래스 템플릿이 선언 되었을 때, double형 값을 저장하는 Array객체를 선언하는 올바른 문장은?

    1
    2
    3
    4
    5
    6
    7
    8
    
     template <class T>
     class Array {
       T* buf;
       int size;
     public:
       Array(int s) : size(s) { buf = new T[s]; }
       T& operator [] (int i) { return buf[i]; }
     };
    

    a. Array<double> dArr(20);

    • 클래스템플릿이름<클래스이름> 형태로 템플릿을 사용함



정리 하기


  • 컨테이너 클래스는 스택, 큐, 배열, 리스트 등과 같이 객체를 저장하는 클래스를 의미함
  • 템플릿은 클래스, 함수 등을 선언하기 위한 형판임
  • 하나의 클래스 템플릿에 서로 다른 템플릿 인수를 전달하여 사용하면 여러 가지 클래스가 선언된 것과 같음
  • 클래스 템플릿의 템플릿 인수로 전달할 클래스는 클래스 템플릿에서 필요로 하는 메소드들을 포함하고 있어야 함
  • 클래스(또는 자료형) 이름 외에도 함수 이름이나 상수를 템플릿에 인수로 전달할 수 있음

[C++ 프로그래밍] 12강 - 상속

[C++ 프로그래밍] 14강 - 템플릿