이 강의를 보고 배운내용을 정리하였습니다.
class Vector {
string* data;
int capacity;
int length;
public:
Vector(int n = 1);
void push_back(string s);
string operator [] (int i);
void remove(int x);
int size();
~Vector();
};
다음과 같은 코드가 있다고 하자. 이는 string 자료형에 대응하는 벡터이다. 만약 이를 다른 자료형으로 쓰려한다면, class를 다시 선언해야 할 것이다.
코딩할 때 제일 많이 듣는 말중 하나가 중복을 줄이자 이다. 이러한 중복을 줄이기 위해서 나온 방법이 바로 template이다. 틀을 만들어놓고 빈자리에 필요한 값만 채워서 바로 넣어버리는 방식이다.
# include <iostream>
# include <string>
template <typename T>
class Vector {
T* data;
int capacity;
int length;
public:
Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}
void push_back(T s) {
if (capacity <= length) {
T* temp = new T[capacity * 2];
for (int i = 0; i < length; i++) {
temp[i] = data[i];
}
delete[] data;
data = temp;
capacity *= 2;
}
data[length] = s;
length++;
}
T operator[](int i) { return data[i]; }
void remove(int x) {
for (int i = x + 1; i < length; i++) {
data[i - 1] = data[i];
}
length--;
}
int size() { return length; }
~Vector() {
if (data) {
delete[] data;
}
}
};
template < typename T >를 통해서 이러한 틀을 만들어 줄 수 있다. 이 T자리에 자료형을 넣어주면 되는것이다.
int main() {
Vector<int> int_vac;
int_vac.push_back(3);
int_vac.push_back(2);
std::cout << " int Vecotr " << std::endl;
std::cout << "First Element : " << int_vac[0] << std::endl;
std::cout << "Second Element : " << int_vac[1] << std::endl;
Vector<std::string> str_vec;
str_vec.push_back("hello");
str_vec.push_back("world");
std::cout << " std::string vector " << std::endl;
std::cout << "First Element : " << str_vec[0] << std::endl;
std::cout << "Second Element : " << str_vec[1] << std::endl;
}
이런식으로 작성하게 되면, int_vac 은 int형 자료를, str_vac은 str 자료를 가지게 된다.
만약
template <typename A, typename B, typename C>
class Specialization {]
이라는 템플릿이 있다고 하자. 이때, A는 int 형, C는 string 이런식으로 따로 처리를 원한다면
template <typename B>
class Specialization <int, B, string> {]
같은 식으로 따로 지정을 해주면 된다. (위에는 범용 템플릿을 써줌
만약 따로 템플릿 전달 인자를 설정하지 않았더라도
template <>
class Specialization <bool> {]
template 선언은 해줘야한다.
#include <iostream>
#include <string>
template <typename T>
T max(T& a, T& b) {
return a > b ? a : b;
}
int main() {
int a = 1;
int b = 2;
std::cout << "Max : " << max(a, b) << std::endl;
double c = 1.0;
double d = 2.0;
std::cout << "Max : " << max(c, d) << std::endl;
}
함수 역시 비슷한 방식으로 template화 할 수있다.
이를 가지고 bubblesort를 구현할 수 있다.
# include <iostream>
# include <string>
template <typename T>
class Vector {
T* data;
int capacity;
int length;
public:
Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}
void push_back(T s) {
if (capacity <= length) {
T* temp = new T[capacity * 2];
for (int i = 0; i < length; i++) {
temp[i] = data[i];
}
delete[] data;
data = temp;
capacity *= 2;
}
data[length] = s;
length++;
}
T operator[](int i) { return data[i]; }
void remove(int x) {
for (int i = x + 1; i < length; i++) {
data[i - 1] = data[i];
}
length--;
}
int size() { return length; }
void swap(int i, int j) {
T temp = data[i];
data[i] = data[j];
data[j] = temp;
}
~Vector() {
if (data) {
delete[] data;
}
}
};
template <typename Cont>
void bubble_sort(Cont& cont) {
for (int i = 0; i < cont.size(); i++) {
for (int j = i + 1; j < cont.size(); j++) {
if (cont[i] > cont[j]) {
cont.swap(i, j);
}
}
}
}
int main() {
Vector<int> int_vec;
int_vec.push_back(3);
int_vec.push_back(4);
int_vec.push_back(5);
int_vec.push_back(8);
int_vec.push_back(9);
int_vec.push_back(2);
std::cout << "before Sorting " << std::endl;
for (int i = 0; i < int_vec.size(); i++) {
std::cout << int_vec[i] << " ";
}
std::cout << std::endl;
bubble_sort(int_vec);
std::cout << "before Sorting " << std::endl;
for (int i = 0; i < int_vec.size(); i++) {
std::cout << int_vec[i] << " ";
}
std::cout << std::endl;
}
위의 vector는 기존의 쓰던 vector와 동일하지만, bubblesort에 사용하기위해 swap함수를 추가로 구현하였다.
이런 bubblesort를 역순정렬, 혹은 다른 기준으로 바꿔서 정렬하려면 어떻게 해야할까?
이럴때 사용하는 방법이 함수 객체이다.
# include <iostream>
# include <string>
template <typename T>
class Vector {
T* data;
int capacity;
int length;
public:
Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}
void push_back(T s) {
if (capacity <= length) {
T* temp = new T[capacity * 2];
for (int i = 0; i < length; i++) {
temp[i] = data[i];
}
delete[] data;
data = temp;
capacity *= 2;
}
data[length] = s;
length++;
}
T operator[](int i) { return data[i]; }
void remove(int x) {
for (int i = x + 1; i < length; i++) {
data[i - 1] = data[i];
}
length--;
}
int size() { return length; }
void swap(int i, int j) {
T temp = data[i];
data[i] = data[j];
data[j] = temp;
}
~Vector() {
if (data) {
delete[] data;
}
}
};
template <typename Cont, typename Comp>
void bubble_sort(Cont& cont, Comp& comp) {
for (int i = 0; i < cont.size(); i++) {
for (int j = i + 1; j < cont.size(); j++) {
if (!comp(cont[i], cont[j])) {
cont.swap(i, j);
}
}
}
}
struct comp1 {
bool operator()(int a, int b) { return a > b; }
};
struct comp2 {
bool operator()(int a, int b) { return a < b; }
};
int main() {
comp1 comp1;
comp2 comp2;
Vector<int> int_vec;
int_vec.push_back(3);
int_vec.push_back(4);
int_vec.push_back(5);
int_vec.push_back(8);
int_vec.push_back(9);
int_vec.push_back(2);
std::cout << "before Sorting " << std::endl;
for (int i = 0; i < int_vec.size(); i++) {
std::cout << int_vec[i] << " ";
}
std::cout << std::endl;
bubble_sort(int_vec, comp1);
std::cout << "after ASC Sorting " << std::endl;
for (int i = 0; i < int_vec.size(); i++) {
std::cout << int_vec[i] << " ";
}
std::cout << std::endl;
bubble_sort(int_vec, comp2);
std::cout << "after DESC Sorting " << std::endl;
for (int i = 0; i < int_vec.size(); i++) {
std::cout << int_vec[i] << " ";
}
std::cout << std::endl;
}
buublesort함수를 잘 보면 기존의 단순 부등호 비교에서, comp라는 함수를 통해 비교를 수행하는것을 알 수 있다.
comp는 그 내부에서 연산자 ()를 정의하고있다. 즉, 실제로는 함수가 아니다. 하지만 bubblesort안에서는 함수처럼 사용되고 있다.
이러한 것들을 함수 객체(Function Object 혹은 Functor 라고 부른다.)
`
#include <iostream>
template <typename T, int num>
T add_num(T t) {
return t + num;
}
int main() {
int x = 3;
std::cout << "x : " << add_num<int, 5>(x) << std::endl;
}
위의 코드를 잘 살펴보면, template가 int num을 받고 아래의 함수가 이를 인자처럼 쓰는것을 볼 수 있다.
이런식으로 타입이 아닌 인자로 사용될 수 있는 자료형은 제한적으로 존재한다.
- 정수타입 (bool, int, char, long)
- 포인터 타입
- enum 타입
- std::nullptr_t
기존의 케이스에서는 배열을 전달하면 배열의 크기를 잃어버리곤했지만, 새로 생긴 #include < array >를 이용하면 문제없이 사용할 수 있다.
처음에 인자에 기본값을 줄 수 도 있다.
#include <iostream>
template <typename T, int num = 3>
T add_num(T t) {
return t + num;
}
int main() {
int x = 3;
std::cout << "x : " << add_num<int>(x) << std::endl;
}
이런식으로 작성하면 6이라는 결과를 받을 수 있다.
혹은 Compare를 쓰지 않고 대소비교를 하기 위해 이런식으로도 작성할 수 있다.
template <typename T, typename Comp>
T min(T a, T b) {
Comp comp;
if (comp(a, b)) {
return a;
}
return b;
}