본문 바로가기
c, c++/c++로 시작하는 객체지향 프로그래밍

12.1~12.5

by 피스타0204 2024. 4. 25.

12.1 

c++에서는 제네릭 유형을 사용하여 템플릿 함수와 클래스를 정의할 수 있습니다. 이 템플릿 함수와 클래스는 다양한 데이터 유형을 하나의 코드로 작성하는 것이 가능하게 해줍니다. 이 장에서는 함수 템플릿과 클래스 템플릿의 정의 방법, 구체적인 형식으로 템플릿을 사용하는 방법, 제너릭 템플릿의 하나인 vector에 대해 알아볼 것입니다.

 

12.2 템플릿의 기본

다음과 같이 4개의 오버로딩을 사용해야 하는 경우도 템플릿을 사용하면 한 개의 식으로 바꿀 수 있습니다. 이를 통해 공간절약을 할 수 있고 프로그램의  유지관리가 쉬워집니다. 

int maxValue(int value1, int value2) {
if (value1 > value2)
return value1;
else
return value2;
}
double maxValue(double value1, double value2) {
if (value1 > value2)
return value1;
else
return value2;
}
char maxValue(char value1, char value2) {
if (value1 > value2)
return value1;
else
return value2;
}
string maxValue(string value1, string value2) {
if (value1 > value2)
return value1;
else
return value2;
}

 

generic type은 int, double,char, string같은 모든 유형에 적용이 가능합니다.

template<typename T>나 template<class T>로 정의한 후, T라는 이름으로 사용할 수 있습니다.

typename 이름 이나 class 이름 으로 사용할 수 있지만 typename이 더 권장됩니다.

#include <iostream>
using namespace std;

template<typename T>

T maxValue(T value1, T value2) {
	if (value1 > value2)
		return value1;
	else
		return value2;
}

int main() {
	cout << "Maximum between 1 and 3 is "<< maxValue(1,3) <<endl;
	cout << "Maximum between 1.5 and 0.3 is "<< maxValue(1.5,0.3)<<endl;
	cout << "Maximum between 'A' and 'N' is"<<maxValue('A','N')<<endl;
	cout << "Maximum between \"NBC\" and \"ABC\" is "<<maxValue(string("NBC"),string("ABC"))<<endl;
	cout << "Maximum between \"NBC\" and \"ABC\" is " << maxValue("NBC", "ABC"); 
	//c문자열이라서 배열의 주소를 비교하여 ABC를 반환한다는 데 결과로 NBC가 나옴
	return 0;
}

 

generic 함수도 매개변수를 참조에 의해 전달할 수 있습니다.

#include <iostream>
using namespace std;

template<typename T>

//참조에 의한 전달이므로 const로 바꾸어주어야 합니다.
T maxValue(const T& value1, const T& value2) {
	if (value1 > value2)
		return value1;
	else
		return value2;
}

int main() {
	cout << "Maximum between 1 and 3 is "<< maxValue(1,3) <<endl;
	cout << "Maximum between 1.5 and 0.3 is "<< maxValue(1.5,0.3)<<endl;
	cout << "Maximum between 'A' and 'N' is"<<maxValue('A','N')<<endl;
	cout << "Maximum between \"NBC\" and \"ABC\" is "<<maxValue(string("NBC"),string("ABC"))<<endl;
	return 0;
}

 

checkpoint)

12.1) 값에 의해 전달한 maxValue함수에서 maxValue(1,1.5)같이 다른 유형의  두개 인수를 갖는 함수를 호출할 수 있는가?

아니오. 서로 다른  typename을 선언해주어야 합니다.

12.2) 값에 의해 전달한 maxValue함수에서  maxValue("NBC", "ABC")와 같이 두 개의 문자열 인수를 갖는 함수를 호출할 수 있는가? 

maxValue(Circle(2),Circle(3))과 같이 두 개의 원 객체 인수를 갖는 함수를 호출할 수 있는가?

maxValue가 아닌 다른 함수에서는 가능하다.

12.3) template<typename T>는 template<class T>로 대체 될 수 있는가?

12.4) 형식 매개변수에 대해 키워드가 아닌 식별자를 사용하여 이름을 지정할 수 있는가?

12.5) 형식 매개변수는 원시 유형이나 객체 유형이 될 수 있는가?

12.6) 다음 코드에서 잘못된 부분이 무엇인가?

#include <iostream>
using namespace std;

template<class T>

T maxValue(T value1, T value2) {
	int result;
	if (value1 > value2)
		result= value1;
	else
		result = value2;
	return result;
}

int main() {
	cout << "Maximum between 1 and 3 is "<< maxValue(1,3) <<endl;
	cout << "Maximum between 1.5 and 0.3 is "<< maxValue(1.5,0.3)<<endl;
	cout << "Maximum between 'A' and 'N' is"<<maxValue('A','N')<<endl;
	cout << "Maximum between \"NBC\" and \"ABC\" is "<<maxValue(string("NBC"),string("ABC"))<<endl;

	return 0;
}

int result로 하면 int 유형만 반환할 수 있다. T result로 바꿔야 한다.

 

12.7) 다음과 같은 max-value함수를 정의할 때, maxValue(1,2.5), maxValue( 1.4,2.5), maxValue( 1.5,2)를 호출할 때 반환되는 값은 무엇인가? 

#include <iostream>
using namespace std;

template<typename T1, typename T2>

T1 maxValue(T1 value1, T2 value2) {

	if (value1 > value2)
		return value1;
	else
		return value2;
}

int main() {
	cout <<  maxValue(1,2.5) <<endl;
	cout << maxValue(1.4,2.5)<<endl;
	cout << maxValue(1.5,2)<<endl;


	return 0;
}

앞에 있는 매개변수를 기준으로 템플릿 함수의 전체 타입이 정해지기 때문에 첫번째는 1과 (int)2.5를 비교해서 2가 나왔다.

 

12.3)  예제 :제네릭 정렬

#include <iostream>
using namespace std;

template<typename T>
void sort(T list[], int listSize) {
	for (int i = 0; i < listSize; i++) {
		T currrentMin = list[i];
		int currentMinIndex = i;
		for (int j = i + 1; j < listSize; j++) {
			if (currrentMin > list[j]) {
				currrentMin = list[j];
				currentMinIndex = j;
			}
		}
		if (currentMinIndex != i)
		{
			list[currentMinIndex] = list[i];
			list[i] = currrentMin;
		}
	}
}

template<typename T>
void printArray(const T list[], int listSize) {
	for (int i = 0; i < listSize; i++) {
		cout << list[i] << " ";
	}
	cout << endl;
}
int main() {
	int list1[] = { 3,5,1,0,2,8,7 };
	sort(list1, 7);
	printArray(list1, 7);

	double list2[] = { 3.5,0.5,1.4,0.4,2.5,1.8,4.7 };
	sort(list2, 7);
	printArray(list2, 7);

	string list3[] = { "Atlanta", "Denver","Chicago","Dallas" };
	sort(list3, 4);
	printArray(list3, 4);


	return 0;
}

템플릿 함수를 사용하기 전 매번 형식 매개변수를 지정해주어야 합니다. 예를 들어 위의 코드에서 printArray함수 전에 template<typename T>를 쓰지 않으면 빌드오류가 발생합니다.

 

checkpoint)

12.8) 다음 코드가 틀린 이유를 말하시오.

#include <iostream>
using namespace std;

template<typename T>
void Swap(T& var1, T& var2) {
	T temp = var1;
	var1 = var2;
	var2 = temp;
}
int main() {
	int v1 = 1;
	int v2 = 2;
	Swap(v1, v2);
	cout << v1 << " " << v2 << endl;
		 
	double d1 = 1;
	double d2 = 2;
	Swap(d1, d2);
	cout << d1 << " " << d2 << endl;

	Swap(v1, d1);
	Swap(1, 2);
	return 0;
}

Swap(v1, d1);

서로 다른 형식끼리 바꾸려 했다.
Swap(1, 2);

상수끼리는 바꿀수 없다.(const)

두 코드를 뺐을 때에는 정상적으로  2 1\n2 1이 출력되었다.

12.4) 클래스 템플릿

클래스도 템플릿으로 정의할 수 있습니다. 하지만 클래스의 멤버함수( 생성자, 인스턴스 함수)는 앞에 템플릿 접두어를 반드시 두어야 합니다.

10.9 ▼

#include <iostream>
using namespace std;
template<typename T>
class StackOfIntegers {
public:
	StackOfIntegers();
	bool isEmpty() const;
	T peek() const;
	void push(T value);
	T pop();
	int getsize() const;
private:
	T elements[100];
	int size;
};
template<typename T>
StackOfIntegers<T>::StackOfIntegers() {
	size = 0;
}
template<typename T>
bool StackOfIntegers<T>::isEmpty() const {
	if (size == 0)return true;
	else return false;
}
template<typename T>
T StackOfIntegers<T>::peek()  const {
	return elements[size - 1];
}
template<typename T>
void StackOfIntegers<T>::push(T value) {
	elements[size] = value;
	size++;
}
template<typename T>
T StackOfIntegers<T>::pop() {
	return elements[--size];
}
template<typename T>
int StackOfIntegers<T>::getsize() const {
	return size;
}


int main() {
	StackOfIntegers<int> stack;

	for (int i = 0; i < 10; i++) {
		stack.push(i);
	}
	while (!stack.isEmpty()) {
		cout << stack.pop() << " ";
	}
	return 0;
}


template<typename T>
void printStack(StackOfIntegers<T>& stack) {
	while (!stack.isEmpty()) {
		cout << stack.pop() << " ";
	}
	cout << endl;
}

int main() {
	StackOfIntegers<string> stack;

	stack.push("abc");
	stack.push("def");
	printStack(stack);
	return 0;
}

 

일반함수도 매번 template<typename T>를 해주어야 합니다.

 

추가로 형식 매개변수와 함께 비형식 매개변수도 정할 수 있습니다.

template<typename T, int capacity>

Stack<string, 500> stackName;

이런 식으로 사용할 수도 있습니다.

 

checkpoint)

12.9) 클래스 정의에서 각 함수에 대해 켐플릿 접두어를 사용해야 하는가? 클래스 구현에서 사용해야 하는가?

클래스 구현에서 사용한다.

12.10) 다음 코드에서 잘못된 부분은 무엇인가?

template<typename T =int>
void printArray(const T list[], int arraySize){
	for(int i=0; i<arraySize; i++){
    	cout << list[i] << " " ;
    }
    cout <<endl;
}

template<typename T =int>

12.11) 다음 코드에서 잘못된 부분은 무엇인가?

#include <iostream>
using namespace std;
template<typename T>
class StackOfIntegers {
public:
	StackOfIntegers();
	bool isEmpty() const;
	T peek() const;
	void push(T value);
	T pop();
	int getsize() const;
private:
	T elements[100];
	int size;
};

StackOfIntegers<T>::StackOfIntegers() {
	size = 0;
}
bool StackOfIntegers<T>::isEmpty() const {
	if (size == 0)return true;
	else return false;
}

T StackOfIntegers<T>::peek()  const {
	return elements[size - 1];
}

void StackOfIntegers<T>::push(T value) {
	elements[size] = value;
	size++;
}

T StackOfIntegers<T>::pop() {
	return elements[--size];
}

int StackOfIntegers<T>::getsize() const {
	return size;
}


void printStack(StackOfIntegers<T>& stack) {
	while (!stack.isEmpty()) {
		cout << stack.pop() << " ";
	}
	cout << endl;
}

int main() {
	StackOfIntegers<string> stack;

	stack.push("abc");
	stack.push("def");
	printStack(stack);
	return 0;
}

 

중간에 template <typename T>를 써줘야 한다.

 

12.12) stack 클래스에 대한 템픞릿 접두어가 template<typename T = string>일 때 문자열 스택을 생성할 수 있는가?

있다. 아래와 같이 가능하다.

template<typename T = string>
void printArray(StackOfIntegers<T>& stack, int arraySize) {
	for (int i = 0; i < arraySize; i++) {
		cout << stack.pop() << " ";
	}
	cout << endl;
}

int main() {
	StackOfIntegers<string> stack;

	stack.push("abc");
	stack.push("def");
	printArray(stack, stack.getsize());
	return 0;
}

 

 

12.5) stack 클래스 개선

동적으로 할당하도록 바꿔보았다.

#include <iostream>
using namespace std;
template<typename T>
class StackOfIntegers {
public:
	StackOfIntegers();
	bool isEmpty() const;
	T peek() const;
	void push(T value);
	T pop();
	int getsize() const;
private:
	T* elements;
	int size;
	int capacity;
	void ensureCapacity();
};
template<typename T>
StackOfIntegers<T>::StackOfIntegers():size(0),capacity(16) {
	elements = new T[capacity];
}
template<typename T>
bool StackOfIntegers<T>::isEmpty() const {
	if (size == 0)return true;
	else return false;
}
template<typename T>
T StackOfIntegers<T>::peek()  const {
	return elements[size - 1];
}
template<typename T>
void StackOfIntegers<T>::push(T value) {
	ensureCapacity();
	elements[size] = value;
	size++;
}
template<typename T>
T StackOfIntegers<T>::pop() {
	return elements[--size];
}
template<typename T>
int StackOfIntegers<T>::getsize() const {
	return size;
}
template<typename T>
void StackOfIntegers<T>::ensureCapacity() {
	if (size >= capacity) {
		T* old = elements;
		capacity = 2 * size;
		elements = new T[size * 2];
		for (int i = 0; i < size; i++) {
			elements[i] = old[i];
		}
		delete[] old;
	}
}
template<typename T>
void printArray(StackOfIntegers<T>& stack, int arraySize) {
	for (int i = 0; i < arraySize; i++) {
		cout << stack.pop() << " ";
	}
	cout << endl;
}

int main() {
	StackOfIntegers<string> stack;

	stack.push("abc");
	stack.push("def");
	printArray(stack, stack.getsize());
	return 0;
}

 

checkpoint)

12.13) 위의 코드delete[] old;를 delete old;로 바꿀 경우 무슨 문제가 발생하는 가?

배열이 삭제가 안됨

 

12.6) c++ 벡터 클래스

배열은 배열 크기가 고정된다는 제한이 있습니다. 이를 해결하기 위해 c++에서는 vector 클래스를 제공합니다. vector는 필요한 경우 크기를 자동으로 증가시킬 수 있습니다.

 vector<타입> 이름 

으로 선언할 수 있습니다.

예를 들어 vector<int> intVector;이나 vector<string> stringVector로 사용할 수 있습니다.

기본값으로 채워진 초기 벡터도 생성할 수 있습니다. 

vector<int> intVector(10); 으로 기본값이 0인 크기 10의 벡터를 생성합니다.

'c, c++ > c++로 시작하는 객체지향 프로그래밍' 카테고리의 다른 글

15.5~15.9) 상속과 "다형성"  (0) 2024.05.09
15.1~15.4, 15.8) 상속  (0) 2024.05.02
11.6~11.15  (0) 2024.04.12
11.1~11.5  (1) 2024.04.04
10.3~10.10  (2) 2024.03.28