반응형
[문과 코린이의 IT 기록장] C++ - 클래스 템플릿(Class Template) (Point 클래스 템플릿과 배열 클래스 템플릿, 특정
템플릿 클래스의 객체를 인자로 받는 일반함수의 정의와, friend 선언)
* 이 포스팅의 '3.배열 클래스의 템플릿화'를 기반으로 확장된 예제를 풀어나가보자 함.
1. Point 클래스 템플릿과 배열 클래스 템플릿
Case 1 )
[ PointTemplate.h ]
#pragma once
#ifndef __POINT_TEMPLATE_H_
#define __POINT_TEMPLATE_H_
template <typename T>
class Point
{
private:
T xpos, ypos;
public:
Point(T x = 0, T y = 0);
void ShowPosition()const;
};
template<typename T>
Point<T>::Point(T x, T y) :xpos(x), ypos(y) { }
template<typename T>
void Point<T>::ShowPosition()const {
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
#endif
[ ArrayTemplate.h ]
#pragma once
#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;
/* 깊은 복사가 진행될 수 있도록, 복사생성자와 대입연산자를 private 맴버로 두면서 복사와 대입을 막는다.*/
// 배열은 저장소의 일종이며, 저장소에 저장된 데이터는 '유일성'이 보장되어야 하기 때문에, 대부분의 경우 배열에서의 복사는 불필요하거나 잘못된 일로 간주하곤 한다.
BoundCheckArray(const BoundCheckArray& arr) { } // 복사생성자
BoundCheckArray& operator=(const BoundCheckArray& arr) { } // 대입연산자
public:
BoundCheckArray(int len); // 생성자
T& operator[] (int idx); // 배열 연산자
T operator[](int idx)const; // const 배열 연산자
int GetArrLen() const; // arrlen : 배열의 길이 반환 함수
~BoundCheckArray(); // 소멸자
};
template<typename T>
BoundCheckArray<T>::BoundCheckArray(int len) :arrlen(len) { // 생성자
arr = new T[len]; // 힙에 배열만큼의 메모리 공간 마련
}
template<typename T>
T& BoundCheckArray<T>::operator[] (int idx) { // 배열 연산자
/* iarr[i] = iarr.operator[](i) */
if (idx < 0 || idx >= arrlen) { // 잘못된 배열 접근을 막기 위한, 안전성 보장 코드
cout << "Array index out of bound exception" << endl;
exit(1); // 에러시 강제 종료
}
return arr[idx]; // 배열 요소의 참조값이 반환됨
}
template<typename T>
T BoundCheckArray<T>::operator[] (int idx) const { // const 맴버함수 추가 배열 연산자
// const 매개변수가 있는 함수가 사용될 예정인 경우, operator[]함수 또한 const로 사용되어야 한다.
// 따라서 이러한 경우 따로 정의를 해 줄 필요가 있다.
if (idx < 0 || idx >= arrlen) {
cout << "Array index out of bound exception" << 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
[ Main ]
#include<iostream>
#include "ArrayTemplate.h"
#include "PointTemplate.h"
using namespace std;
int main() {
BoundCheckArray<Point<int>> oarr1(3);
// Point<int> 템플릿 클래스의 객체를 저장할 수 있도록
oarr1[0] = Point<int>(3, 4);
oarr1[1] = Point<int>(5, 6);
oarr1[2] = Point<int>(7, 8);
for (int i = 0; i < oarr1.GetArrLen(); i++) {
oarr1[i].ShowPosition();
}
BoundCheckArray<Point<double>>oarr2(3);
// Point<double> 템플릿 클래스의 객체를 저장할 수 있도록
oarr2[0] = Point<double>(3.14, 4.31);
oarr2[1] = Point<double>(5.09, 6.07);
oarr2[2] = Point<double>(7.82, 8.54);
for (int i = 0; i < oarr2.GetArrLen(); i++) {
oarr2[i].ShowPosition();
}
typedef Point<int>* POINT_PTR;
BoundCheckArray<POINT_PTR> oparr(3);
// POINT_PTR (= Point<int>*) 템플릿 클래스의 객체를 저장할 수 있도록
oparr[0] = new Point<int>(11, 12); // 주소값을 저장해야 하므로 동적할당 필요
oparr[1] = new Point<int>(13, 14);
oparr[2] = new Point<int>(15, 16);
for (int i = 0; i < oparr.GetArrLen(); i++) {
oparr[i]->ShowPosition();
// oparr[i]는 주소값을 담고 있으므로 포인터로 요소를 선택하는 -> 필요
}
delete oparr[0]; delete oparr[1]; delete oparr[2];
return 0;
}
1) Point 템플릿을 사용하지 않은 경우
BoundCheckArray<Point> arr(3);
2) Point 템플릿 사용한 경우
BoundCheckArray<Point<int>> oarr(3);
BoundCheckArray<Point<int>*> oparr(3);
2. 특정 템플릿 클래스의 객체를 인자로 받는 일반함수의 정의와, friend 선언
- 위와 같은 Point<int>, Point<double>과 같은 템플릿 클래스의 자료형을 대상으로도, 템플릿이 아닌 일반함수의 정의가 가능하다.
- 또한 클래스 템플릿 내에서 이러한 함수를 대상으로, friend 선언도 가능하다.
Case 2 )
#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 ShowPosition() const {
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
/* Point<int> 객체의 +연산자를 오버로딩 하는 일반함수 (함수 템플릿 X)
* cf) 함수 템플릿 특수화 : Point<int> operator+<Point<int>> 이런 식으로 이루어져야 함
*/
friend Point<int>operator+(const Point<int>&, const Point<int>&);
// Point<int> 템플릿 함수 사용한 객체, 이 객체끼리 operator+할 일이 있다면
friend ostream& operator<<(ostream& os, const Point<int>& pos);
// Point<int> 템플릿 함수를 사용한 객체가 cout을 할 일이 있다면
};
// Point<int> 템플릿 함수 사용한 객체, 이 객체끼리 operator+할 일이 있다면
Point<int> operator+(const Point<int>& pos1, const Point<int>& pos2) {
return Point<int>(pos1.xpos + pos2.xpos, pos1.ypos + pos2.ypos);
}
// Point<int> 템플릿 함수를 사용한 객체가 cout을 할 일이 있다면
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; // 특정<int> 템플릿 클래스의 객체로 인자를 받는 일반함수 실행
cout << pos1 << pos2 << pos3;
return 0;
}
[ 다른 게시물들을 더 보고싶다면? ]
* 유의사항 - 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다. - 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다. - 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :) |
반응형