C++ Chapter 13.2 : 클래스 템플릿
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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
로 넣어놨다. - 꼭 이름이
typename
과T
일 필요는 없다. 근데 보통 이렇게 씀!
주의사항 📢 클래스 템플릿은 그 자체로 클래스는 아니다. 클래스 틀일 뿐, 클래스인건 아니다.
구체화
#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
타입으로 구체화 됐다.- 이말은 즉, 이 클래스를 인스턴스화 할 때 메모리를 얼만큼 할당 받아야 하는지 알게 되었다는 얘기다.✨
- MyArray 클래스 템플릿이
- 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 파일들끼리
링킹
이 된다.
- 각각의 cpp 파일들이 독립적으로
- 문법 체크 + static 한 영역들 메모리 할당 일을 수행한다.
- 컴파일시 메모리를 정의하려면 구체적인 자료형을 알아야지만 얼만큼 메모리를 할당할지 알 수 있는데
- 📜MyArray.cpp 파일은 템플릿 타입만 달랑 있어서 어느 정도로 할당해야 할지 알 수가 없기 때문에 print() 바디 부분이 메모리에 정의가 되지 못 하고 그냥 넘어가게 된다.
- 따라서 링킹시 print()의 바디 부분을 알 수 없어 링킹 에러가 난다.
- 컴파일 에러는 없다. 📜main.cpp에서 📜MyArray.h를 include 하고 있기 때문에 print()라는 멤버 함수가 있다는건 알고 있기 때문.
- 헤더 파일은 컴파일 하지 않으며
- 컴파일
- 👉 📜MyArray.cpp 파일을 컴파일 할 때, 어떤 자료형으로 클래스를 구체화 해야하는지 몰라서 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;
}
};
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기