출처 :
- 명품 C++ Programming (저자 황기태)
- 객체지향프로그래밍
일반화와 템플릿
- 제네릭 또는 일반화
- 함수나 클래스를 일반화시키고, 매개 변수 타입을 지정하여 틀에서 찍어내듯이 함수나 클래스 코드를 생산하는 기법
- 템플릿
- 변수나 매개 변수의 타입만 다르고, 코드 부분이 동일한 함수를 일반화 시킴
- 함수나 클래스를 일반화하는 도구
- template 키워드 사용
- 제네릭 타입 - 일반화를 위한 데이터 타입
template <class T> template <typename T> template <class T> void myswap (T & a, T & b) { T tmp; tmp = a; a = b; b = tmp; }
템플릿으로부터의 구체와
- 구체화 - 템플릿의 제네릭 타입에 구체적인 타입 지정
- 템플릿 함수로부터 구체화된 함수의 소스 코드 생성
- 컴파일 될 때 T에 int를 대입하여 구체화된 소스 코드 생성(구체화) 후 실행
제네릭 myswap() 함수 만들기
#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle(int radius=1) { this->radius = radius; }
int getRadius() { return radius; }
};
template <class T>
// 자료형이 T로 같기 때문에
// myswap(int &, double &) 처럼 다르게 구체화할 수 없다.
void myswap(T & a, T & b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
int main() {
int a=4, b=5;
myswap(a, b);
cout << "a=" << a << ", " << "b=" << b << endl;
double c=0.3, d=12.5;
myswap(c, d);
cout << "c=" << c << ", " << "d=" << d << endl;
Circle donut(5), pizza(20);
myswap(donut, pizza);
cout << "donut반지름=" << donut.getRadius() << ", ";
cout << "pizza반지름=" << pizza.getRadius()<< endl;
}
구체화 오류
- 매개 변수의 제네릭 타입이 동일해야 한다.
템플릿 장점과 제네릭 프로그래밍
- 장점
- 함수 코드의 재사용
- 단점
- 컴파일러에 따라 지원하지 않을 수 있음
- 컴파일 오류 메시지가 빈약해 디버깅이 어려움
- 제네릭 프로그래밍 - 다른 언어에서도 활용되고 있다.
제네릭 add() 함수 연습
#include <iostream>
using namespace std;
template <class T>
T add(T data [], int n) { // 배열 data에서 n개의 원소를 합한 결과를 리턴
T sum = 0;
for(int i=0; i<n; i++) { sum += data[i]; }
return sum; // sum와 타입과 리턴 타입이 모두 T로 선언되어 있음
}
int main() {
int x[] = {1,2,3,4,5};
double d[] = {1.2, 2.3, 3.4, 4.5, 5.6, 6.7};
cout << "sum of x[] = " << add(x, 5) << endl; // x는 int로 구체화
cout << "sum of d[] = " << add(d, 6) << endl; // d는 double로 구체화
}
제네릭 mcopy()
#include <iostream>
using namespace std;
// 두 개의 제네릭 타입 T1, T2를 가지는 copy()의 템플릿
// T1과 T2의 자료형은 다를 수 있다.
template <class T1, class T2>
// src[]의 n개 원소를 dest[]에 복사하는 함수
void mcopy(T1 src [], T2 dest [], int n) {
for(int i=0; i<n; i++)
// T1 타입의 값을 T2 타입으로 변환한다.
dest[i] = (T2)src[i];
}
int main() {
int x[] = {1,2,3,4,5};
double d[5];
char c[5] = {'H', 'e', 'l', 'l', 'o'}, e[5];
mcopy(x, d, 5); // int x[]의 원소 5개를 double d[]에 복사
mcopy(c, e, 5); // char c[]의 원소 5개를 char e[]에 복사
for(int i=0; i<5; i++) cout << d[i] << ' '; // d[] 출력
cout << endl; // 1 2 3 4 5
for(int i=0; i<5; i++) cout << e[i] << ' '; // e[] 출력
cout << endl; // H e l l o
}
템플릿 함수보다 중복 함수가 우선
#include <iostream>
using namespace std;
template <class T>
void print(T array [], int n) {
for(int i=0; i<n; i++)
cout << array[i] << '\\t';
cout << endl;
}
// char 배열을 출력하기 위한 함수 중복
// 중복된 print() 함수가 우선 바인딩
void print(char array [], int n) {
for(int i=0; i<n; i++)
// array[i]를 int 타입으로 변환하여 정수 출력
cout << (int)array[i] << '\\t';
cout << endl;
}
int main() {
int x[] = {1,2,3,4,5};
double d[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
print(x, 5); // 템플릿으로 구체화한 함수 호출
print(d, 5); // 템플릿으로 구체화한 함수 호출
char c[5] = {1,2,3,4,5};
print(c, 5); // char 배열을 숫자로 출력하는 중복 함수 호출
}
제네릭 클래스 만들기
- 제네릭 클래스 선언부와 구현부를 모두 template으로 선언
#include <iostream>
using namespace std;
template <class T>
class MyStack {
int tos;// top of stack
T data [100]; // T 타입의 배열. 스택의 크기는 100
public:
MyStack();
void push(T element); // element를 data [] 배열에 삽입
T pop(); // 스택의 탑에 있는 데이터를 data[] 배열에서 리턴
};
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>();
p->push('a');
cout << p->pop() << endl;
delete p;
}
C++ 표준 템플릿 라이브러리 STL 활용
- Standard Tempate Library
- STL 구성
- 컨테이너
- 데이터를 담아두는 자료 구조를 표현한 클래스
- 리스트, 큐, 스택, 맵, 셋 벡터
- iterator (포인터)
- 컨테이너 원소에 대한 포인터
- 컨테이너의 원소들을 순회하면서 접근하기 위해 만들어짐
- 알고리즘
- 복사, 검색, 삭제, 정렬 등의 기능을 구현한 템플리 ㅅ함수
- 컨테이너의 멤버 함수가 아님
- 컨테이너
vector 컨테이너
- 가변 길이 배열을 구현한 제네릭 클래스
- 원소의 저장, 삭제, 검색 등 지원
- 벅터에 저장된 원소 인덱스로 접근 가능
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v; // 정수만 삽입 가능한 벡터 생성
v.push_back(1); // 벡터에 정수 1 삽입
v.push_back(2); // 벡터에 정수 2 삽입
v.push_back(3); // 벡터에 정수 3 삽입
for(int i=0; i<v.size(); i++)
cout << v[i] << " "; // 1 2 3
cout << endl;
v[0] = 10; // 벡터의 첫 번째 원소를 10으로 변경
int n = v[2]; // n에 3이 저장
v.at(2) = 5; // 벡터의 3 번째 원소를 5로 변경
for(int i=0; i<v.size(); i++)
cout << v[i] << " "; // 10 2 5
cout << endl;
}
iterator 사용
- 컨테이너의 원소를 가리키는 포인터
- 반복자
// vevtor<int>의 iterator다~ 라고 구체적으로 컨테이너 지정
vector<int>::iterator it;
it = v.begin();
iterator 예제
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
vector<int>::iterator it; // 벡터 v의 원소에 대한 포인터 it 선언
for(it=v.begin(); it != v.end(); it++) {
int n = *it; // it가 가리키는 원소 값 리턴
n = n*2;
*it = n; // it가 가리키는 원소에 값 쓰기
}
for(it=v.begin(); it != v.end(); it++)
cout << *it << ' ';
cout << endl;
}
map 컨테이너
- #include <map>
- (키, 값)의 쌍을 원소로 저장하는 제네릭 컨테이너
- 키로 값 검색
주요 멤버
- inser(pair<> &element
- at(key_type& key)
- begin()
- end()
- empty()
- find(key_type& key)
- erase(iterator it)
- size()
- operator[key_type & key] ()
- operator=()
map으로 영한 사전 만들기
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
map<string, string> dic;
dic.insert(make_pair("love", "사랑"));
dic.insert(make_pair("apple", "사과"));
dic["cherry"] = "체리";
cout << "저장된 단어 개수 " << dic.size() << endl;
string eng;
while (true) {
cout << "찾고 싶은 단어>> ";
getline(cin, eng); // 사용자로부터 키 입력
if (eng == "exit")
break;
if(dic.find(eng) == dic.end()) // eng '키'를 끝까지 찾았는데 없음
cout << "없음" << endl;
else
cout << dic[eng] << endl; // dic에서 eng의 값을 찾아 출력
}
cout << "종료합니다..." << endl;
}
STL 알고리즘 사용하기
- 템플릿 함수
- 전역함수 - STL 컨테이너 클래스의 멤버 함수가 아님
- iterator와 함께 작동
sort()
- sort(시작 원소의 주소, 마지막 원소의 “다음” 주소)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v; // 정수 벡터 생성
cout << "5개의 정수를 입력하세요>> ";
for(int i=0; i<5; i++) {
int n;
cin >> n;
v.push_back(n); // 키보드에서 읽은 정수를 벡터에 삽입
}
// v.begin()에서 v.end() 사이의 값을 오름차순으로 정렬
// sort() 함수의 실행 결과 벡터 v의 원소 순서가 변경됨
sort(v.begin(), v.end());
vector<int>::iterator it; // 벡터 내의 원소를 탐색하는 iterator 변수 선언
for(it=v.begin(); it != v.end(); it++) // 벡터 v의 모든 원소 출력
cout << *it << ' ';
cout << endl;
}
auto, 람다는 시험 안나옴
'[전공]' 카테고리의 다른 글
[명품 C++] 11 C++ 입출력 시스템 (2) | 2022.06.06 |
---|---|
[명품 C++] 9장 가상 함수와 추상 클래스 (1) | 2022.06.06 |
[명품 C++] 08 상속 (0) | 2022.06.06 |
[명품 C++] 07 프렌드와 연산자 중복 (0) | 2022.06.05 |
[명품 C++] 6장 함수 중복과 static 멤버 (3) | 2022.06.05 |