C++

11주차 스터디

kchabin 2022. 12. 4. 11:16

함수 중복의 약점 - 중복 함수의 코드 중복


일반화와 템플릿

제네릭(generic)
- 함수나 클래스를 일반화, 매개 변수 타입을 지정하여 틀에서 찍어내듯이 함수나 클래스 코드를 생산하는 기법
템플릿
함수나 클래스를 일반화하는 도구
template 키워드로 함수나 클래스 선언
변수나 매개 변수의 타입만 다르고, 코드 부분이 동일한 함수를 일반화시키
제네릭 타입 :
일반화를 위한 데이터 타입


템플릿 선언
template <class T>
template <typename T>
template <class T1, class T2, class T3>


구체화(specialization)
- 템플릿의 제네릭 타입에 구체적인 타입 지정
- 템플릿 함수로부터 구체화된 함수의 소스코드 생성


template <class T>
void myswap(T &a, T &b)
{
     T tmp;
     tmp = a;
     a = b;
     b = tmp;
}

int main() 
{
     int a =4, b=5;
     myswap(a,b);
}

//구체화
void myswap(int &a, int &b)
{
     int tmp;
     tmp = a;
     a = b;
     b = tmp;
}

제네릭 타입에 구체적인 타입 지정 시 주의
템플릿으로부터 myswap(int &, double &) 함수를 구체화할 수 없다.
-> 매개변수의 타입이 다르면 안됨.

템플릿 장점

- 함수 코드의 재사용 -> 높은 SW 생산성과 유용성.

템플릿 단점

- 포팅에 취약함 : 컴파일러에 따라 지원하지 않을 수 있음
- 컴파일 오류 메시지 빈약, 디버깅에 많은 어려움

제네릭 프로그래밍(generic programming)

- 일반화 프로그램
- 제네릭 함수나 클래스를 이용하는 기법
- C++에서 STL(Standard Template Library) 제공, 활용.
- 보편화 추세 -> java, c# 등 많은 언어에서 활용함.

예제



배열을 출력하는 print() 템플릿 함수의 문제점

template <class T> 
void print(T array [], int n)
{
   for(int i=0l i<n; i++)
      cout << array[i] << '₩t';
   cout << endl;
}

int x[] = {1,2,3,4,5};
print(x, 5); //T가 int 타입으로 구체화

char c[5] = {1,2,3,4,5};
print(c,5); //T가 char 타입으로 구체화

char로 구체화되면 정수 1, 2, 3, 4, 5에 대한 그래픽 문자가 숫자 대신 출력되는 문제가 발생한다.

이를 해결하기 위해 템플릿 함수보다 중복 함수가 우선인 것을 활용한다.

void print(char array[], int n) //char 배열을 출력하기 위한 함수 중복
{
    for(int i=0; i<n; i++)
       cout << (int)array[i] << '\t';  //array[i]를 int 타입으로 변환하여 정수 출력
    cout << endl;

위와 같이 템플릿 함수와 중복된 print()함수를 만들면 print(c, 5)에는 중복된 print() 함수가 우선 바인딩된다.




제네릭 스택 클래스 만들기

#include <iostream>
using namespace std;

template <class T>
class mystack {
	int tos;
	T data[100];
public:
	mystack();
	void push(T element);
	T pop();
};

template <class T>
mystack<T>::mystack() {//생성자
	tos = -1; //스택은 비어 있음
}

template <class T>
void mystack<T>::push(T element) {
	if (tos == 99)
	{
		cout << "stack full";
		return;
	}
	tos++;
	data[tos] = element;
}
template <class T>
T mystack<T>::pop()
{
	T retData;
	if (tos == -1)
	{
		cout << "stack empty";
		return 0; //오류 표시
	}
	retData = data[tos--];
	return retData;
}

int main()
{
	mystack<int> iStack;
	iStack.push(3);
	cout << iStack.pop() << endl;

	mystack<double> dStack;
	dStack.push(3.5);
	cout << dStack.pop() << endl;

	mystack<char>* p = new mystack<char>(); //char만 저장하는 스택
	p->push('a');
	cout << p->pop() << endl;
	delete p;
}


제네릭 스택의 제네릭 타입을 포인터나 클래스로 구체화하는 예

class Point
{ 
   int x, y;
public:
   Point(int x=0; int y=0) { this->x=x; this->y=y; }
   void show() { cout << x << ',' << y << endl; }
   
int main()
{
   MyStack<int*> ipStack; //int*만을 저장하는 스택
   int* p = new int [3];
   for(int i=0; i<3; i++) p[i] = i*10;
   ipStack.push(p);  //포인터 푸시
   int *q = ipStack.pop() //포인터 팝
   for(int i=0; i<3; i++) cout << q[i] << ''; //화면 출력
   cout << endl;
   delete [] p;
   
   MyStack<Point> pointStack; //Point 객체 저장 스택
   MyStack<Point*> pStack; //Point* 포인터 스택
   pStack.push(new Point(10,20)); //Point 객체 푸시
   Point* pPoint = pStack,pop(); //Point 객체의 포인터 팝
   pPoint->show(); 
   
   MyStack<string> stringStack; //문자열만 저장하는 스택
   string s="C++";
   stringStack.push(s);
   stringStack.push("java");
   cout << stringStack.pop() << '';
   cout << stringStack.pop << endl;  //java C++ 출력

'C++' 카테고리의 다른 글

12주차 스터디  (0) 2022.12.11
10주차 스터디  (0) 2022.11.24
9주차 스터디  (0) 2022.11.19
8주차 스터디  (0) 2022.11.13
껄껄껄 7주차 스터디  (0) 2022.11.05