2023.01.24
#14-1 추가 확장
01. 추가 확장
저번에 했던 ch.13의 파일을 좀 더 변형? 확장?시키는 내용~~
저번의 Point클래스를 템플릿화 하였었는데, 이 클래스 템플릿을 기반으로 Point<int>같이 또 템플릿 클래스의 객체를 저장하려면 어떻게 해야할까?
답은!!
그냥 평소 하던대로 하면 됨.
단지 괄호가 늘 뿐임.
BoundCheckArray<Point<int>> oarr(50); //이런식으로!
BoundCheckArray<Point<int>*> oparr(50); //포인터는 이런식으로! 참고로 포인터는 아래처럼도 됨
typedef Point<int>* POINT_PTR; BoundCheckArray<POINT_PTR> oarr(50);
/* Point.h */
#ifndef __POINT_H_
#define __POINT_H_
#include <iostream>
using namespace std;
template <typename T>
class Point
{
private:
T xpos, ypos;
public:
Point(T x = 0, T y = 0); //생성자 외부 정의 예정
void ShowPos() const; //멤버함수 외부 정의 예정
};
template <typename T>
Point<T>::Point(T x, T y) : xpos(x), ypos(y) {} //생성자 외부 정의
template <typename T>
void Point<T>::ShowPos() const //멤버함수 외부 정의
{
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
#endif
/* ArrayTemplate.h */
#ifndef __ARRAY_TEMPLATE_H_
#define __ARRAY_TEMPLATE_H_
#include <iostream>
#include <cstdlib>
using namespace std;
template <typename T>
class BoundCheckArray
{
private:
T* arr; //템플릿 배열
int arrlen;
BoundCheckArray(const BoundCheckArray& arr) {} //생성자
BoundCheckArray& operator=(const BoundCheckArray& arr) {} //대입연산자
public:
BoundCheckArray(int len);
T& operator[] (int idx);
T operator[] (int idx) const;
int GetArrLen() const;
~BoundCheckArray(); //소멸자
};
template <typename T>
BoundCheckArray<T>::BoundCheckArray(int len) : arrlen(len)
{
arr = new T[len]; //동적할당
}
template <typename T>
T& BoundCheckArray<T>::operator[] (int idx)
{
if (idx < 0 || idx >= arrlen)
{
cout << "오류: 배열 범위 밖에 있음" << endl;
exit(1);
}
return arr[idx];
}
template <typename T>
T BoundCheckArray<T>::operator[] (int idx) const
{
if (idx < 0 || idx >= arrlen)
{
cout << "오류: 배열 범위 밖에 있음" << endl;
exit(1);
}
return arr[idx];
}
template <typename T>
int BoundCheckArray<T>::GetArrLen() const
{
return arrlen;
}
template <typename T>
BoundCheckArray<T>::~BoundCheckArray()
{
delete[]arr;
}
#endif
/* ArrayMain.cpp */
#include <iostream>
#include "ArrayTemplate.h"
#include "Point.h"
using namespace std;
int main()
{
cout<<"Point<int> 객체 저장"<<endl;
BoundCheckArray<Point<int>> oarr(3);
oarr[0] = Point<int>(3, 4);
oarr[1] = Point<int>(5, 6);
oarr[2] = Point<int>(7, 8);
for (int i = 0; i < oarr.GetArrLen(); i++)
oarr[i].ShowPos();
cout << endl;
cout << "Point<double>형 객체 저장" << endl;
BoundCheckArray<Point<double>> oarr2(3);
oarr2[0] = Point<double>(3.14, 4.31);
oarr2[1] = Point<double>(5.09, 6.07);
oarr2[2] = Point<double>(7.82, 8.14);
for (int i = 0; i < oarr2.GetArrLen(); i++)
oarr2[i].ShowPos();
cout << endl;
cout << "Point<int>*형 객체 저장" << endl;
typedef Point<int>* POINT_PTR;
BoundCheckArray<POINT_PTR> oarr3(3);
oarr3[0] = new Point<int>(11, 12); //포인터는 바로 동적할당하면서 초기화!
oarr3[1] = new Point<int>(13, 14);
oarr3[2] = new Point<int>(15, 16);
for (int i = 0; i < oarr3.GetArrLen(); i++)
oarr3[i]->ShowPos(); //포인터라서 그 안에 있는게 아니니까 가리키는 걸로!
delete oarr3[0]; delete oarr3[1]; delete oarr3[2]; //포인터 동적할당 해제
return 0;
}
02. 특정 템플릿 클래스의 객체를 인자로 전달 받는 일반함수의 정의와 friend 선언
- Point<int>와 같은 템플릿 클래스의 자료형을 대상으로도 템플릿이 아닌 일반 함수의 정의 가능하고, 템플릿 내에서 이걸 대상으로 friend 선언도 가능함!
#include <iostream>
using namespace std;
template <typename T>
class Point
{
private:
T xpos, ypos;
public:
Point(T x=0, T y=0): xpos(x), ypos(y) {}
void ShowPos() const
{
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
/* 클래스 템플릿에서도 일반함수에 대해ㅐ friend 선언이 가능함!! */
friend Point<int> operator+(const Point<int>&, const Point<int>&);
friend ostream& operator<<(ostream& os, const Point<int>& pos);
};
Point<int> operator+(const Point<int>& pos1, const Point<int>& pos2)
{
return Point<int>(pos1.xpos + pos2.xpos, pos1.ypos + pos2.ypos);
}/* 함수 템플릿처럼 보이나, 이는 연산자 오버로딩 일반함수임!
그냥 템플릿 클래스의 객체 둘을 인자로 전달받을 뿐 */
ostream& operator<<(ostream& os, const Point<int>& pos)
{
os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;
return os;
}//마찬가지로 그냥 연선자 오버로딩하는 일반함수임.
int main()
{
Point<int> pos1(2, 4);
Point<int> pos2(4, 8);
Point<int> pos3 = pos1 + pos2;
cout << pos1 << pos2 << pos3;
return 0;
}
출력하면 마찬가지로
[2, 4]
[4, 8]
[6, 12]
절대 귀찮아서 안찍은게 아니라 그냥 간단해서 그런 것이다
03. 클래스 템플릿의 특수화
- ch 13 에서 함수 템플릿을 특수화 한 이유: 특정 자료형에 대해 구분이 되는 다른 행동을 보이기 위해(ex: 문자열 const 선언 여부로 찾아가는 함수가 달라짐)
-ch 14 에서 클래스 템플릿을 특수화 하는 이유: 특정 자료형을 기반으로 생성된 객체에 대해 구분이 되는 이하생략
한마디로 그냥 이번에는 객체에 대한 행동을 다르게 하기 위해 특수화 시킨다는 말임.
<특수화>시키는 방법이란?!
template <typename T>
class SoSimple
{
public:
T SimpleFunc(T num) { . . . }
};
이런 대충 생긴 함수가 존재한다고 치면
template <>
class SoSimple<int>
{
public:
int SimpleFunc(int num) { . . . }
};
/* 이렇게 int형에 대해 특수화가 되고 나면,
SoSimple<int> obj; 로 객체생성시 특수화된 템플릿 클래스 SoSimple<int>를 대상으로 객체가 생성됨. */
라는 뜻이다.
한마디로 그냥 처음부터 따로 타입을 고정시킨다는 말인데...
이럴거면 왜 굳이 템플릿을 쓰는 거지?
뭐 이 함수가 쓸 데가 굉장히 많고 다양한데 굳이 int형이 따로 필요한 상황이 생겼나보다.
암튼 예제 ㄱ
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
template <typename T>
class Point
{
private:
T xpos, ypos;
public:
Point(T x = 0, T y = 0) : xpos(x), ypos(y) {}
void ShowPos() const
{
cout << '[' << xpos << ", " << ypos << endl;
}
};
template <typename T>
class SimpleDataWrapper
{
private:
T mdata;
public:
SimpleDataWrapper(T data): mdata(data){}
void ShowDataInfo()
{
cout << "Data: " << mdata << endl;
}
};
template <> //위 템플릿 특수화 시작함.
class SimpleDataWrapper <char*>
{
private:
char* mdata;
public:
SimpleDataWrapper(const char* data)
{
mdata = new char[strlen(data) + 1]; //strlen() 은 NULL제외 글자수만 하므로
strcpy(mdata, data);
}
void ShowDataInfo()
{
cout << "String: " << mdata << endl;
cout << "Length: " << strlen(mdata) << endl;
}
~SimpleDataWrapper() { delete[]mdata; }
};
template <>
class SimpleDataWrapper <Point<int>>
{
private:
Point<int> mdata;
public:
SimpleDataWrapper(int x, int y) : mdata(x, y) {}
void ShowDataInfo()
{
mdata.ShowPos();
}
};
int main()
{
SimpleDataWrapper<int> iwrap(170);
/* int형에 대해서는 특수화 안했으므로 컴파일 될 때 별도로
SimpleDataWrapper<int>가 만들어지고, 이 클래스를 기반으로 객체가 생성됨 */
iwrap.ShowDataInfo();
SimpleDataWrapper<char*> swrap("Class Template Kingbadne");
/* char형에 대해서는 특수화 했으므로 별도의 템플릿 클래스 생성x,
이미 정의한 특수화 템플릿 클래스의 객체가 생성됨 */
swrap.ShowDataInfo();
SimpleDataWrapper<Point<int>> poswrap(8, 14);
/* 마찬가지로 이미 정의 되었으니 ㄱㄱㄱ */
poswrap.ShowDataInfo();
return 0;
}
<부분 특수화>도 할 수 있다는데?!
그렇다.
template <typename T1, typename T2>
class Simple { . . . }
또 이렇게 대충 생긴 클래스가 있을 때
/* T1, T2 각각 특수화 시키기 */
template<>
class Simple<char, int> { . . . }
/* T2만 부분 특수화 시키기 */
template<typename T1>
class Simple<T1, int> { . . . }
이런 식으로 할 수 있음
또 예제 ㄱㄱ
#include <iostream>
#include <cstring>
using namespace std;
template <typename T1, typename T2>
class Simple
{
public:
void WhoAreYou()
{
cout << "size of T1: " << sizeof(T1) << endl;
cout << "size of T2: " << sizeof(T2) << endl;
cout << "<typename T1, typename T2>" << endl << endl;
}
};
template <>
class Simple<int, double>
{
public:
void WhoAreYou()
{
cout << "size of int: " << sizeof(int) << endl;
cout << "size of double: " << sizeof(double) << endl;
cout << "<int, double>" << endl << endl;
}
};
template <typename T1>
class Simple<T1, double>
{
public:
void WhoAreYou()
{
cout << "size of T1: " << sizeof(T1) << endl;
cout << "size of double: " << sizeof(double) << endl;
cout << "<T1, double>" << endl << endl;
}
};
int main()
{
Simple<char, double> obj1;
obj1.WhoAreYou();
Simple<int, long> obj2;
obj2.WhoAreYou();
Simple<int, double> obj3;
obj3.WhoAreYou();
return 0;
}
출력결과를 보면 알 수 있는 점!!
int main()에서 마지막 3번째 부분이 <int, double>로
전체 특수화된 class Simple<int, double> 이랑
부분 특수화된 class Simple<T1, double> 에
둘 다 해당될 수 있는데요 출력결과를 보시면 전체 특수화로 된다는 걸 알 수 있습니다.
따라서 두가지 모두 해당되는 객체생성 문장에서는 전체 특수화가 부분 특수화보다 우선시 된다-!!
입니다.
04. 템플릿 인자
지금까지 템플릿을 정의할 때 보면,,, 결정되지 않은 자료형을 의미하는 용도로 사용되는
T, T1, T2같은 문자 있죠?
이것을 '템플릿 매개변수'라 합니다.
이러한 매개변수 자리에는 재밌게도,,,(@: 안재밋어요)
마치 함수처럼, '변수의 선언'이 올 수 있다는 것입니다-!!
template <typename T, int len>
class SimpleArray
{
private:
T arr[len];
public:
T& operator[] (int idx)
{
return arr[idx];
}
그래서 이를 기반으로
SimpleArray<int, 10> arr1;
SimpleArray<double, 1> arr2;
이런 객체 생성이 가능하단 것이죠,,,
이렇게 len씨에게 전달된 인자 10과 1은 해당 템플릿 클래스에서 상수처럼 사용됩니다. 걍 치환 적용된다는 뜻임
그래서 컴파일러에 의해서 SimpleArray<int, 10>형 템플릿 클래스와 SimpleArray<double, 1>형 템플릿 클래스가 각각 생성됩니다!!
class SimpleArray<int, 10>
{
private:
int arr[10];
public:
int& operator[] (int idx)
{
return arr[idx];
}
class SimpleArray<double, 1>
{
private:
double arr[1];
public:
double& operator[] (int idx)
{
return arr[idx];
}
이런 식으로 말이죠,,,
중요한 점은 이 둘이 서로 「다른 자료형의 클래스로 구분된다는 것」입니다.
당연함
근데 왜 말하냐면?
바로,,, 아래와 같은 의문점을 해결하기에 필요한 키포인트이기 때문입니다.
@: 이의있음!! 굳이 이따구로 변수선언 귀찮게 하지말고 걍 늘 그래왔듯이 생성자 이니셜라이저로 한번에 초기화해서 보내면 안되는지?!
네. 안됩니다. 왜냐하면 컴파일러가,,,,,,,
int main()
{
SimpleArray<int, 10> i10arr;
SimpleArray<int, 1> i1arr;
i10arr=i1arr; //컴파일 에러!!!
. . .
}
컴파일러: 자료형도 같고 길이도 같은 배열 객체에 대해서만 대입 및 복사를 허용할거임. 만약 생성자를 이용해서 배열의 길이를 정하게 했다면, 길이가 같은 배열에 대해서만 대입을 허용하기 위해 추가적인 코드 삽입을 해야함. 그 말은 님 CPU가 바빠져서... (더보기)
라는 입장이기 때문입니다.
템플릿 매개변수는 디폴트 값 지정도 가능하다?!
#include <iostream>
using namespace std;
template <typename T=int, int len=7> //디폴트 값 지정
class SimpleArray
{
private:
T arr[len];
public:
T& operator[] (int idx) { return arr[idx]; }
SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref) //대입연산자
{
for (int i = 0; i < len; i++)
arr[i] = ref.arr[i];
return *this;
}
};
int main()
{
SimpleArray<> arr; //디폴트 값을 이용한 객체 생성
for (int i = 0; i < 7; i++)
arr[i] = i + 1;
for (int i = 0; i < 7; i++)
cout << arr[i] << " ";
cout << endl;
return 0;
}
출력결과: 1 2 3 4 5 6 7
가능합니다.
여기서 포인트는 템플릿 매개변수에 디폴트 값이 지정되어도 템플릿 클래스의 객체생성을 의미하는 <>기호는 반드시 추가되어야 한다는 점입니다. 그 안을 비워둘지라도 말이죠.
이럴수록 대체 왜 쓰는지 의문이지만 뭐 언젠가 쓰일 날이 오겠거니 하고 넘어갑시다.
05. 템플릿과 static
static이란?!

딱 한번 초기화된 상태에서 그 값을 계속 유지하는 변수였습니다.
tmplate <typename T>
void ShowStatic()
{
static T num=0;
num+=1;
cout << num << " ";
}
또 이 대충 생긴 함수 템플릿을 살펴보면, 안에 지역변수 num이 static으로 선언된 것을 볼 수 있습니다.
그리고 컴파일러는 위의 함수 템플릿을 기반으로 여러 템플릿 함수들을 만들어 냅니다.
void ShowStatic<int>()
{
static int num=0;
num+=1;
cout << num << " ";
}
void ShowStatic<long>()
{
static long num=0;
num+=1;
cout << num << " ";
}
이런 느낌으로 말이죠,,,
그래서 사용자가 int main()안에서
ShowStatic<int>(); ShowStatic<long>(); ShowStatic<double>();
이런 식으로 부르면 다 똑같은 숫자로 출력이 되는 것입니다...
이러한 static 멤버변수는 변수가 선언된 클래스의 객체간 공유가 가능한 변수입니다.
가령 main에서 obj1.addmem(2); obj2.addmem(3);을 해주게 되면 템플릿 클래스 내에서 둘이 같은 변수를 공유하므로 5가 출력이 될 수 있는 것처럼 말이죠,,,하하!!!끝났다!!!!!!!!!!!!

이상 끝~!!!!
와 지금 다시 보니까 진짜 얼마나 재미 없었으면,,, 말투가 점점 이상해지고 있음
더 웃긴점: 원본은 진짜 정신나가서 이것도 거의 말 뺀거임 (원본: 밈 대잔치)
하여튼 오랜만에 다시 보니까 그 두꺼운 책 다 끝낸게 신기하네요
그리고 결과: 학교에서 템플릿 안 씀
'✨언어 > c++' 카테고리의 다른 글
[ch 13] 클래스 템플릿 (1) | 2024.06.25 |
---|