타입을 일반화하는 방식
타입별로 서로 다른 함수를 재작성할 필요가 없어짐
void Sort(T scores[])
{
//원소의 타입을 T라고 하자
}
OOP에서는 INT 같은 TYPE을 CLASS TYPE으로 객체화하기 때문에 TYPENAME만 써도 됨
template <typename T1, typename T2, typename T3 ....>
template <class T1, class T2, class T3 ....>void Sort(T val, T2 val2, T3 val3...) { T x; T2 y; }
#include <iostream>
void Sort(int input[], int size)
{
for ( int i = 0; i < size-1; i++ )
{
for ( int j = i+1; j < size; j++ )
{
if ( input[i] > input[j] )
{
int temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
}
}
void Sort(float input[], int size)
{
for ( int i = 0; i < size - 1; i++ )
{
for ( int j = i + 1; j < size; j++ )
{
if ( input[i] > input[j] )
{
float temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
}
}
void Sort(bool input[], int size)
{
for ( int i = 0; i < size - 1; i++ )
{
for ( int j = i + 1; j < size; j++ )
{
if ( input[i] > input[j] )
{
bool temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
}
}
class Student
{
public:
int scores;
};
//클래스에 대한 sort
void Sort(Student input[], int size)
{
for ( int i = 0; i < size - 1; i++ )
{
for ( int j = i + 1; j < size; j++ )
{
if ( input[i].scores > input[j].scores )
{
Student temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
}
}
//무슨타입이든 하나의 정렬함수로 모든 타입이 가능하도록
//힌트 : 배열은 포인터
//포인터중 타입과 상관없는것 : void
enum Type
{
INT,
FLOAT,
BOOL,
DOUBLE,
CHAR,
};
void Sort(void *input, int size,Type t)
{
switch ( t )
{
case INT:
break;
case FLOAT:
break;
case BOOL:
break;
case DOUBLE:
break;
case CHAR:
break;
default:
break;
}
}
//이런 방법을 쉽게 하는 것
//템플릿
//타입을 일반화(Generic Programming)
template<typename T>
void Sort(T input[], int size)
{
for ( int i = 0; i < size - 1; i++ )
{
for ( int j = i + 1; j < size; j++ )
{
if ( input[i] > input[j] )
{
T temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
}
}
//일반화시킨 정렬
int main()
{
int scores[] {10,20,30,40,50};
Sort(scores, 5);
for ( int i = 0; i < 5; i++ )
{
std::cout << scores[i] << std::endl;
}
float scores2[] {1.1f,2.2f,3.3f,10.0f,5.0f};
//오버로딩으로
Sort(scores2, 5);
char scores[] {10,20,30,40,50}; //이렇게 모든 타입으로 가능
}
컴파일러가 타입을 명시하지 않아도 자동으로 찾아줌
template <typename MyType1>
//타입의 이반화 Genaralization
MyType1 Function(MyType1 val1,MyType1 val2)
{
MyType1 result;
result = val1 + val2;
return result;
}
class Students
{
public:
int scores;
int operator+()
{
return;
//students사이에 연산지 제공되면 가능함
//하지만 없으면 에러
//template과 연산자 오버로딩이 조화가 잘됨
}
};
int main()
{
//표기법
//특수화
std::cout << Function<int>(3, 4) << std::endl;
//int를 mytype이라고 하자고 명시해줌
//컴파일러가 발전하면서 template argument deduction
//매개변수 추론
Function(3, 4);
//int임을 알수있음=>추론 가능함=>생략가능
//템플릿임을 알려주기 위해서 생략안하기도 함
//어떻게 알 수 있나??
//컴파일러가 프로그래머가 수동으로 타입을 변환하듯이 컴파일함
//코드를 복사해서 붙여넣기하는 작업을 컴파일러가 대신하는 작업
Students a, b;
a.scores = 10;
b.scores = 20;
}
template <typename T>
class Myclass
{
T member;
T function(T val);
};
주의사항
- 자동 아니고 AI 아니고 복사 붙여넣기임
이 템플릿을 사용할 수록 코드의 양이 늘어남 -> 최적화 문제- 템플릿은 선언과 정의가 같은 파일에 있어야 함
-> 템플릿은 실제로 존재하는 타입이 아니고 특수화를 할 때 존재하게 되는 것 그래서 정의가 같이 있어야함
-> 타입으로 특수화를 해야함
-> 컴파일러가 복붙을 하기 때문에 생기는 문제template <typename T> class MyClass { T mValue; public: void F(T arg); }; void MyClass<T> :: F(T arg) { } 선언과 정의를 분리하는 방법 같은 곳에 있어야함
- #include "MyClass.hpp"
template의 정의를 hpp에 옮길 수 있음
선언과 정의를 무조건 분리해야하는 사람을 위한 기능
*.h *.hpp *.cpp
실수할 확률이 적어짐
virtual쓸때 자식에 override쓰기
class Parent
{
public:
virtual void F();
};
class child : public Parent
{
public:
void F() override;
//부모에서 virtual 자식에서 override를 쌍으로 쓰는 것을 기억하기
};
#include<iostream>
class MyClass
{
public:
int mInt;
MyClass()
{
std::cout << "나일까?" << std::endl;
}
explicit MyClass(int val):mInt {val}
{
std::cout << "불렸나?" << std::endl;
//생성자가 없이 가능한가??
//생성자를 제공하면 기본생성자가 없음
//그러면 생성자를 만든 것 아닐까>
//생성자를 없애면 main안 코드가 불가능(생성자가 없어서)
}
};
int main()
{
MyClass c = MyClass(1); //생성자가 없으면 불가능 MyClass c = 1;과 같음
//MyClass c = 1; 생성자가 없으면 불가능(명시적 생성자가 있으면 에러)
//위 초기화 방식을 막는 것이 explicit 키워드
//
MyClass c {1};
//멤버를 바꾸는 초기화 방식 = 생성자 없어도 불가능함
//표기법상으로는 어떤 것인지 애매함
//구분하기 위해서 명시적 생성자라는 기능이 존재함
MyClass c3(3);
}