본문 바로가기
2CHAECHAE 학교생활/객체지향프로그래밍(C++)

[ 객체지향프로그래밍(C++) 12주차 ② ]

by 2CHAE._.EUN 2022. 5. 29.

[ 12주차 - 2 ]

 

일반 함수

 

일반 함수 :  여러 가지 자료형에 적용될 일반적인 연산들

 

일반 함수를 생성함으로써 데이터의 종류와는 독립적으로 알고리즘의 특성을 정의할 수 있다.

일반 함수가 정의되면 컴파일러는 함수가 실행될 때 사용되는 자료형을 위한 정확한 코드를 자동으로 만들어낸다.

 

일반 함수 키워드는 template를 사용함으로써 생성된다.

 

template < class Ttype > ret-type func-name( parameter-list ) {
	...
}

type : 함수에 의해 사용될 자료형을 나타내는 이름

 

#include <iostream>
using namespace std;

template <class X> void swapargs(X& a, X& b) {
	
	X temp;

	temp = a;
	a = b;
	b = temp;

}

 

X는 자리만 잡아주는 일반 자료형이고, 컴파일러가 특정한 버전의 함수를 생성할 때 실제 자료형으로 대체된다.

X에는 int, float, double 등이 올 수 있으며 실행시에 결정이 된다.

 

 

하나의 일반 자료형을 갖는 함수

 

#include <iostream>
using namespace std;

template <class X> void swapargs(X& a, X& b) {
	
	X temp;

	temp = a;
	a = b;
	b = temp;


}

int main() {

	int i = 20, j = 20;
	double x = 10.1, y = 23.3;
	char a = 'x', b = 'z';

	cout << "Original i, j : " << i << " " << j << "\n";
	cout << "Original x, y : " << x << " " << y << "\n";
	cout << "Original a, b : " << a << " " << b << "\n";

	swapargs(i, j); // 정수 교환
	swapargs(x, y); // 실수 교환
	swapargs(a, b); // 문자 교환

	cout << "Swapped i, j : " << i << " " << j << "\n";
	cout << "Swapped x, y : " << x << " " << y << "\n";
	cout << "Swapped a, b : " << a << " " << b << "\n";

	return 0;

}

//Original i, j : 20 20
//Original x, y : 10.1 23.3
//Original a, b : x z
//Swapped i, j : 20 20
//Swapped x, y : 23.3 10.1
//Swapped a, b : z x

 

 

두 개의 일반 자료형을 갖는 함수

 

#include <iostream>
using namespace std;

template <class type1, class type2> void myfunc(type1 x, type2 y) {

	cout << x < " " << y << "\n";

}

int main() {

	myfunc(10, "hi");

	myfunc(0.23, 10L);

	return 0;

}

//10 hi
//0.23 10

 

template 문에서 콤마로 분리되는 리스트를 사용함으로써 두 개 이상의 일반 자료형을 정의할 수 있다.

 

 

일반 함수 중복하기

 

#include <iostream>
using namespace std;

template <class X> void swapargs(X& a, X& b) {

	X temp;

	temp = a;
	a = b;
	b = temp;

	cout << "Inside template swapargs\n";

}

// int 형을 위해 swapargs의 일반 함수 버전을 중복한다.
// 중복된 함수는 특정한 버전과 관련하여 일반 함수를 중복한다.
void swapargs(int& a, int& b) {

	int temp;

	temp = a;
	a = b;
	b = temp;

	cout << "Inside swapargs int specialization\n";

}

 

매개 변수가 둘 다 int일 경우의 중복된 함수가 구체적으로 명시되어 있다면 매개변수가 int일 경우의 함수가 먼저 호출이 되고 다른 매개 변수 형들은 template 함수가 호출이 된다.

 

#include <iostream>
using namespace std;

template <class X> void swapargs(X& a, X& b) {

	X temp;

	temp = a;
	a = b;
	b = temp;

	cout << "Inside template swapargs\n";

}

// int 형을 위해 swapargs의 일반 함수 버전을 중복한다.
// 중복된 함수는 특정한 버전과 관련하여 일반 함수를 중복한다.
void swapargs(int& a, int& b) {

	int temp;

	temp = a;
	a = b;
	b = temp;

	cout << "Inside swapargs int specialization\n";

}

int main() {

	int i = 20, j = 20;
	double x = 10.1, y = 23.3;
	char a = 'x', b = 'z';

	cout << "Original i, j : " << i << " " << j << "\n";
	cout << "Original x, y : " << x << " " << y << "\n";
	cout << "Original a, b : " << a << " " << b << "\n";

	swapargs(i, j); // 명시적으로 중복된 swapargs()를 호출
	swapargs(x, y); // 일반 함수 swapargs()를 호출
	swapargs(a, b); // 일반 함수 swapargs()를 호출

	cout << "Swapped i, j : " << i << " " << j << "\n";
	cout << "Swapped x, y : " << x << " " << y << "\n";
	cout << "Swapped a, b : " << a << " " << b << "\n";

	return 0;

}

//Original i, j : 20 20
//Original x, y : 10.1 23.3
//Original a, b : x z
//Inside swapargs int specialization
//Inside template swapargs
//Inside template swapargs
//Swapped i, j : 20 20
//Swapped x, y : 23.3 10.1
//Swapped a, b : z x

 

매개 변수가 정수형일 경우 일반 함수가 명시적으로 중복되었기 때문에 컴파일러는 일반 버전의 swapargs() 함수를 생성하지 않는다. 

 

 

함수 template 중복하기

 

#include <iostream>
using namespace std;

// f() 템플리트의 첫번째 버전
template <class X> void f(X a) {
	cout << "Inside f(X a)\n";
}

// f() 템플리트의 두번째 버전
template <class X, class Y> void f(X a, Y b) {
	cout << "Inside f(X a, Y b)\n";
}

int main() {

	f(10); //f(X)를 호출한다. 
	f(10, 20); // f(X,Y)를 호출한다.

	return 0;

}

//Inside f(X a)
//Inside f(X a, Y b)

 

template 명세를 중복할 수 있다. 그렇게 하기 위해서 매개변수 리스트에서 다른 것과 구별이 되는 템플리트 버전을 생성한다. 

 

템플리트 함수와 함께 표준 매개변수를 사용하기

 

#include <iostream>
using namespace std;

template <class X> void repeat(X data, int times) {
	do {
		cout << data << "\n";
		times--;
	} while (times);
}

int main() {
	repeat("This is a test", 3);
	repeat(100, 5);
	repeat(99.0 / 2, 4);
	return 0;
}

//This is a test
//This is a test
//This is a test
//100
//100
//100
//100
//100
//49.5
//49.5
//49.5
//49.5

 

첫번째 매개변수는 템플릿 자료형을 사용하고 두번째 매개변수는 일반 자료형이다. 즉, 표준 매개변수와 일반 자료형 매개변수를 템플리트 함수에서 섞어 쓸 수 있다. 

 

매개변수가 반드시 템플리트 자료형이 되어야 하는 것은 아니다.

 

 

일반 클래스

 

일반 클래스를 정의할 때 그 클래스에 의해 사용되는 모든 알고리즘을 정의하는 클래스를 생성한다. 그러나 조작되는 데이터의 실제 자료형은 그 클래스의 객체가 생성될 때 매개변수로 명시된다.

 

일반 클래스는 클래스가 일반화될 수 있는 연산을 사용할 때 유용하다.

 

template <class Ttype> class class-name{
	...
}

Ttype : 자리만 차지하는 형 이름 ( 클래스가 인스턴스화될 때 명시됨 )

 

Ttype에 객체를 생성할 때 대응하는 자료형을 반드시 명시해야한다. 명시를 안해주면 컴파일 에러가 난다.

 

* 일반 클래스의 인스턴스 생성 형식

class-name <type> ob  ( type : 클래스가 동작할 데이터의 형 이름 )

 

일반 클래스의 멤버 함수들은 자동으로 일반 함수가 되므로 template를 사용할 필요는 없다.

 

 

일반 클래스 예

 

#include <iostream>
using namespace std;

const int SIZE = 100;

template < class QType > class queue {
private:
	QType q[SIZE];
	int sloc, rloc;
public:
	queue() {
		sloc = rloc = 0;
	}
	void qput(QType i);
	QType qget();
};

 

QType에는 어떠한 자료형도 사용될 수 있다. 멤버함수들은 다 똑같고 클래스만 QType 자료형이다.

queue에서 삽입, 삭제에 해당하는 자료형은 역시 QType이 되어야한다.

 

// 하나의 원소를 큐에 넣는다.
template <class QType> void queue<QType>::qput(QType i) {
	if (sloc == SIZE) {
		cout << "Queue is full.\n";
		return;
	}
	sloc++;
	q[sloc] = i;
}

// 큐에서 하나의 원소를 얻는다.
template <class QType> QType queue<QType>::qget() {
	if (rloc == sloc) {
		cout << "Queue Underflow.\n";
		return 0;
	}
	rloc++;
	return q[rloc];
}

 

템플리트 클래스 내에 존재하는 멤버 함수들은 템플리트 클래스를 명시해주는 문법을 지켜야한다.

 

#include <iostream>
using namespace std;

const int SIZE = 100;

template < class QType > class queue {
private:
	QType q[SIZE];
	int sloc, rloc;
public:
	queue() {
		sloc = rloc = 0;
	}
	void qput(QType i);
	QType qget();
};

// 하나의 원소를 큐에 넣는다.
template <class QType> void queue<QType>::qput(QType i) {
	if (sloc == SIZE) {
		cout << "Queue is full.\n";
		return;
	}
	sloc++;
	q[sloc] = i;
}

// 큐에서 하나의 원소를 얻는다.
template <class QType> QType queue<QType>::qget() {
	if (rloc == sloc) {
		cout << "Queue Underflow.\n";
		return 0;
	}
	rloc++;
	return q[rloc];
}

int main() {

	queue<int> a, b; // 2개의 정수 큐를 생성한다.
	a.qput(10);
	b.qput(10);
	a.qput(20);
	b.qput(1);

	cout << a.qget() << " ";
	cout << a.qget() << " ";
	cout << b.qget() << " ";
	cout << b.qget() << "\n";

	queue<double> c, d; // 2개의 실수 큐를 생성한다.
	c.qput(10.12);
	d.qput(19.99);
	c.qput(-20.0);
	d.qput(0.986);

	cout << c.qget() << " ";
	cout << c.qget() << " ";
	cout << d.qget() << " ";
	cout << d.qget() << "\n";

	return 0;

}

//10 20 10 1
//10.12 -20 19.99 0.986

 

QType형에는 int queue class나 double queue class를 사용할 수 있다.

 

 

두 개의 일반 자료형을 가진 예

 

#include <iostream>
using namespace std;

template <class Type1, class Type2> class myclass {
	Type1 i;
	Type2 j;
public:
	myclass(Type1 a, Type2 b) {
		i = a;
		j = b;
	}
	void show() {
		cout << i << " " << j << "\n";
	}
};

int main() {

	myclass<int, double> ob1(10, 0.23);
	myclass<char, char*> ob2('X', "This is a test");

	ob1.show(); // int와 double을 보여준다.
	ob2.show(); // char와 char*를 보여준다.

	return 0;

}

 

 

실습문제

 

n값을 받아 n크기의 배열을 동적으로 생성하고 n개의 값을 입력받아 그 배열에 저장하고, 최대값을 출력하는 일반 클래스를( template class)를 작성하라

 

멤버변수 : size, arr[]

멤버함수 : 생성자함수, input(), max()

 

테스트 : main 함수에서 5개의 정수와 3개의 실수에 대해 테스트하기