C++ Chapter 13.2 : 클래스 템플릿

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 13. 템플릿 : 클래스 템플릿

🔔 일반화와 구체화

일반화

📜MyArray.h

#include <iostream>

template <typename T>
class MyArray
{
private:
    int m_length;
    T * m_data;   // ⭐
public:
    MyArray(int length)
    {
        m_data = new T[length]; // ⭐
        m_length = length;
    }

    ~MyArray()
    {
        reset();
    }

    void reset()
    {
        delete [] m_data;
        m_data = nullptr;
        m_length = 0;
    }

    T & operator [] (int index) // ⭐
    {
        assert (index >= 0 && index < m_length);
        return m_data[index];
    }

    int getLength()
    {
        return m_length;
    }

    void print()
    {
        for (int i = 0; i < m_length; ++i)
            std::cout << m_data[i] << " ";
        std::cout << std::endl;
    }
};
  • 여러 자료형으로 일반화할 부분들은 T로 넣어놨다.
  • 꼭 이름이 typenameT일 필요는 없다. 근데 보통 이렇게 씀!

주의사항 📢 클래스 템플릿은 그 자체로 클래스는 아니다. 클래스 틀일 뿐, 클래스인건 아니다.


구체화

#include "MyArray.h"

int main()
{
    MyArray<char> my_array(10);

    for (int i = 0; i < my_array.getLength();++i)
        my_array[i] = i + 65;

    my_array.print();

    return 0;
}
  • MyArray<char> my_array(10);
    • MyArray 클래스 템플릿이 char 타입으로 구체화 됐다.
      • 이말은 즉, 이 클래스를 인스턴스화 할 때 메모리를 얼만큼 할당 받아야 하는지 알게 되었다는 얘기다.✨
  • my_array[i] = i + 65;
    • []연산자 오버로딩이 되어 있어서 객체 이름[]으로 바로 동적 배열 멤버의 원소에 접근할 수 있다.


🔔 클래스 템플릿은 헤더파일, cpp로 분리하지 않는게 좋다.

단순히 헤더 파일, cpp로 분리한 경우 👉 링킹 에러 발생

print() 멤버 함수의 선언과 정의를 각각 헤더파일과 cpp 파일로 분리해보자.

📜MyArray.h

#include <iostream>

template <typename T>
class MyArray
{
    ...  // 다른 부분들은 위와 동일

    void print();  // ⭐ 선언만
};

📜MyArray.cpp

#include "MyArray.h"

template <typename T>
void MyArray<T>::print()
{
    for (int i = 0; i < m_length; ++i)
        std::cout << m_data[i] << " ";
    std::cout << std::endl;
}

📜main.cpp

#include "MyArray.h"

int main()
{
    MyArray<char> my_array(10);

    for (int i = 0; i < my_array.getLength();++i)
        my_array[i] = i + 65;

    my_array.print(); // 💥 링킹 에러 발생

    return 0;
}

컴파일엔 문제가 없으나 💥링킹 에러 발생💥

  • 링킹 에러 이유
    • 👉 📜MyArray.cpp 파일을 컴파일 할 때, 어떤 자료형으로 클래스를 구체화 해야하는지 몰라서 print() 함수의 바디 부분이 메모리에 정의가 되지 않았기 때문이다.
      • 컴파일
        • 헤더 파일은 컴파일 하지 않으며
          • cpp 파일에 내에서 include 한 헤더 파일 내용이 복사될 뿐이다.
        • cpp 파일들만 컴파일한다.
          • 각각의 cpp 파일들이 독립적으로 컴파일 된 이후에
          • 컴파일이 완료된 같은 프로젝트 내의 cpp 파일들끼리 링킹이 된다.
        • 문법 체크 + static 한 영역들 메모리 할당 일을 수행한다.
          • 컴파일시 메모리를 정의하려면 구체적인 자료형을 알아야지만 얼만큼 메모리를 할당할지 알 수 있는데
          • 📜MyArray.cpp 파일은 템플릿 타입만 달랑 있어서 어느 정도로 할당해야 할지 알 수가 없기 때문에 print() 바디 부분이 메모리에 정의가 되지 못 하고 그냥 넘어가게 된다.
          • 따라서 링킹시 print()의 바디 부분을 알 수 없어 링킹 에러가 난다.
            • 컴파일 에러는 없다. 📜main.cpp에서 📜MyArray.h를 include 하고 있기 때문에 print()라는 멤버 함수가 있다는건 알고 있기 때문.


해결 방법 1

📜main.cpp

#include "MyArray.h"
#include "MyArray.cpp"
  • 📜MyArray.cpp 파일도 직접 include 해주는 것이다. 이러면 링킹 에러가 발생하지 않는다.
    • 📜main.cpp에 📜MyArray.cpp의 내용을 복사해오는 것이므로!

그러나 이 방법은 추천하지 않는다. .cpp파일까지 포함하는 일은 프로그램이 커질 시 매우 복잡해진다.


해결 방법 2

explicit instantiation

📜MyArray.cpp

#include "MyArray.h"

template <typename T>
void MyArray<T>::print()
{
    for (int i = 0; i < m_length; ++i)
        std::cout << m_data[i] << " ";
    std::cout << std::endl;
}

template void MyArray<char>::print();
template void MyArray<double>::print();

이렇게 📜MyArray.cpp 내에서 직접 특정 자료형들을 구체화 해두어 📜MyArray.cpp 컴파일시 각각 메모리가 미리 잡히도록 해주는 방법이 있다. char로도, double로도.

#include "MyArray.h"

template <typename T>
void MyArray<T>::print()
{
    for (int i = 0; i < m_length; ++i)
        std::cout << m_data[i] << " ";
    std::cout << std::endl;
}

template class MyArray<char>;
template class MyArray<double>;

이렇게 클래스 단위로 구체화 시킬 수도 있다.


해결 방법 3 : 가장 추천 ✨

가장 추천하는 방법은 그냥 클래스 템플릿은 헤더 파일 내에서 전부 구현하는 것이다. 클래스 템플릿은 선언부, 구현부 각각 헤더파일, cpp 파일로 나누어 작성하지 말고 그냥 헤더 파일내에 선언 + 구현까지 전부 해놓자.

클래스 템플릿은 전부 한 파일 내에서 다 구현해주자.

📜MyArray.h

#include <iostream>

template <typename T>
class MyArray
{

    ...

    void print()
    {
        for (int i = 0; i < m_length; ++i)
            std::cout << m_data[i] << " ";
        std::cout << std::endl;
    }
};


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기