- SOLID원칙: 소프트웨어의 유지/ 보수와 확장성에 도움이 되는 다섯가지 기본 원칙
1) 단일 책임 원칙 (SRP)
- 클래스는 한 가지 기능만 수행해야 하고, 한 가지 이유로만 변경해야 한다
= 클래스를 설계할 때 역할을 복잡하지 않게 해야한다
2) 개방/ 폐쇄 원칙 (OCP)
- 확장에 개방, 수정에 폐쇄
= 동적 바인딩 이용시 프로그램은 새로운 기능을 추가할 수 있는 방법 (확장에 개방)이 생기고, 다른 코드에 파급 효과가 없어 추가되는 기능 외에는 수정이 필요 없음 (수정에 폐쇄)- 추상클래스 (인터페이스) 이용해 구현 가능
- 템플릿 메서드 패턴: 프로그램에서 주요 기능의 흐름은 추상클래스를 이용해 작성하고 이를 상속받아 구현하는 클래스에 따라서 세부동작이 결정되게하는 것
- 소프트웨어 개발과 유지/ 보수에 있어 아주 중요
3) 리스코프 치환 원칙 (LSP)
- 하위클래스는 상위클래스를 대체할 수 있어야 한다
=> 다형성의 동작원리 설명- is-a 관계로 정의된 클래스는 LSP따르는 클래스...
= 부모클래스를 상속받아 구현한 자식클래스는 부모클래스로 업캐스팅 가능
= 자식클래스에서 부모클래스의 멤버함수를 상속받아 오버라이딩하거나 유지해야함
4) 인터페이스 분리원칙 (ISP)
- 단일책임원칙을 인터페이스에 적용한 것
= 인터페이스는 작고 섬세해야하며, 클래스는 필요한 인터페이스 까지만 구현해야한다
5) 의존성 역전 원칙 (DIP)
- 상위 수준 모듈은 하위 수준의 모듈에 의존해서는 안되며 상위/ 하위 수준 모두 추상레이어 (인터페이스)에 의존해야한다
= 구체적인 것보다는 추상적인 것에 의존해야한다, 추상클래스와 인터페이스를 사용해 준수
(개방/ 폐쇄 원칙을 적용하기 위해 사용=> 의존성 역전 원칙 적용시 함께 적용)
1) 함수 템플릿
- 템플릿: 함수 하나로 다양한 형식의 데이터를 같은 알고리즘으로 처리할 수 있게 도와줌
- 템플릿 사용시 매개변수의 데이터 형식을 함수를 작성하는 시점이 아닌 사용하는 시점에 정할 수 있음= 함수 템플릿이 포함된 소스코드를 컴파일하면 해당 함수에 사용된 데이터 형식으로 함수의 실제 구현체가 만들어짐
#include <iostream> #include <string> using namespace std; template <typename T> //템플릿 선언 T data_sum (T operand1, T operade2) {// 함수템플릿 정의 return operade1 + operade2; } int main () { int data1=3, data2=5; double data3= 4.5, data4= 8.9 string data5= "Hello,", data6= "World!"; cout<<data_sum(data1, data2)<<endl; cout<<data_sum(data3, data4)<<endl; cout<<data_sum(data5, data6)<<endl; return 0; } 8 13.4 Hello, World!<typename 매개변수,, >= 임의의 데이터형식= 템플릿 매개변수 => 여러개 선언하려면 typename과 함께 나열
- 템플릿의 선언 방법이나 분문 정의방법은 일반 함수와 같지만함수에 전달할 인자의 데이터 형식을 고정하지 않아도 된다는 특징이 존재
- 한 번 정의해놓으면 다양한 데이터형식을 처리할 수 있지만, 그렇기 때문에 모든 데이터 형식에 대응할 수 있는 알고리즘으로 정의되어야 함
- 템플릿의 인스턴스화: 컴파일러가 함수템플릿을 호출하는 구문을 만나고, 인자로 전달한 값으로 템플릿 매개변수의 데이터 형식을 추론하여 이 형식으로 완성된 함수를 오브젝트 코드로 만드는 과정
- 함수템플릿은 컴파일시점에 데이터 형식별로 템플릿이 인스턴스로 만들어지므로 템플릿을 많이 사용하면 컴파일 시간이 길어짐
- 데이터 형식 추론과 명시적 호출
- 데이터 형식이 명확하지 않으면 추론이 불가능해 컴파일 오류 발생 가능
(ex) 자료형이 맞지 않는 경우)
=> 필요할 경우 명시적 형변환을 해야함- 명시적 형변환: 함수템플릿 호출문에서 인자를 입력하기 전 <>로 데이터 형식을 명시 => 컴파일러가 데이터 형식을 추론하지 않고 해당 형식으로 변환한 후에 바로 인스턴스화
(ex) char형과 string)
- 템플릿 특수화: 특수한 상황일 때만 별도의 함수 템플릿으로 만들어 처리하는 방법 => 특정 데이터 형식만 다른 알고리즘으로 처리하게 만들기 가능
= 목적이 같은 알고리즘을 처리 방법만 다르게 표현
- 명시적 특수화: 모든 템플릿 매개변수를 특정 데이터 형식으로 지정
- 부분 특수화: 일부 템플릿 매개변수만 특정 데이터 형식으로 지정
- 함수 템플릿에서는 명시적 특수화만 사용 가능
=> 템플릿 매개변수를 사용하지 않고 함수에 데이터 형식을 모두 지정
함수이름과 매개변수 개수는 변경하지 않음#include <iostream> #include <string> using namespace std; template <typename T> //템플릿 선언 T data_sum (T operand1, T operade2) {// 함수템플릿 정의 return operade1 + operade2; } template<> double data_sum (double operand1, double operand2) { return (int)operand1 + (int)operand2; } int main () { int data1=3, data2=5; double data3= 4.5, data4= 8.9 string data5= "Hello,", data6= "World!"; cout<<data_sum(data1, data2)<<endl; cout<<data_sum(data3, data4)<<endl; cout<<data_sum(data5, data6)<<endl; return 0; } 8 12 Hello, World!
- 템플릿 특수화를 사용하지 않고 함수를 따로 만들어도 됨!
(그래도 템플릿을 사용하는 소스코드에서 특정 데이터만 다르게 처리해야 할 때는 템플릿 특수화를 이용하는 것이 더 좋음)
2) 클래스 템플릿
- 클래스 템플릿: 템플릿을 클래스에 적용시킨 것
템플릿 매개변수를 활용해 다양한 형식에 대응할 수 있는 범용 클래스를 만드는 방법
클래스 템플릿을 직접 선언하기 보다는 이미 선언된 클래스 템플릿을 이용하는 경우가 더 많음template <typename Type1, typename Type2> class data_package { public: data_package (Type1 first, Type2 second) : first(first), second(second){} private: Type1 first Type2 second; };일반 클래스와 비슷하지만, 범용 데이터 형식을 지정할 자리에 템플릿 매개변수를 사용한다는 특징이 있음
- 템플릿 매개변수는 멤버함수, 멤버변수에 모두 사용/ 함수 템플릿을 만든 것 처럼 멤버함수를 만들면 됨
- 부분 특수화 사용 가능
- 클래스 템플릿으로 객체 생성시 템플릿 매개변수에 사용할 데이터 형식 지정 -> 생성자나 멤버함수에서 템플릿 매개변수에 맞춰 지정한 데이터 형식을 일관되게 유지하며 사용 -> 컴파일러가 지정된 데이터 형식을 사용하는 클래스를 인스턴스화
int main() { data_package<int, double> template_inst1(5, 10.5); data_package<string, int> template_inst2("문자열",10); ...+) C++표준 라이브러리 함수들은 다양한 데이터 형식을 받아서 같은 알고리즘으로 동작하는 클래스 템플릿으로 구성
+) 클래스는 이미 기능을 묶어서 추상화된 범용성을 가지고 있지만, 기능을 추상화해서 표현해도 데이터 형식은 고정해 사용함,,, -> 템플릿 사용해서 데이터 형식까지 범용적으로 만들 수 있음
- 클래스 템플릿에서 형식 추론
- 함수템플릿에서는 컴파일러가 템플릿 매개변수의 데이터 형식을 추론할 수 있지만 클래스 템플릿에서는 객체를 생성할 때 템플릿 매개변수의 형식을 명시해주어야 함 =>> 데이터 형식이 명확하지 않다면 직접 명시해주는 것이 오류를 막는 것에 도움
- 부분특수화: 클래스 템플릿을 인스턴스화할 때 매개변수를 특정 형식의 값으로 처리하는 것
template <typename T> class data_package <string, T>{ public: data_package (string first, T second) : first(first), second(second){}템플릿 객체 생성시 첫번째 인자의 타입을 string으로 하면 부분 특수화가 적용된 class data_package<string, T>가 인스턴스화 됨 => 형식을 특정하여 별도로 처리 가능
+) 클래스 템플릿은 일반 클래스와 거의 모든 동작이 같음,,, 하지만 클래스 템플릿의 선언과 정의를 별도의 파일로 분리하면 안됨 (클래스 템플릿은 선언과 정의가 같이 있어야 함)
- 중첩 클래스 템플릿: 클래스 템플릿 중첩 가능
- 안쪽 클래스를 멤버변수처럼 사용: 멤버변수이므로 바깥쪽 클래스에서 안쪽 클래스의 멤버변수를 선언하고 필요하면 초기화도 진행
- 안쪽 클래스를 독립된 객체로 선언해서 사용: 템플릿이 중첩되어 있기 때문에 바깥쪽 클래스의 템플릿 매개변수도 모두 입력해야함
- 템플릿 매개변수 기본값: 매개변수의 기본값 정의해놓으면 함수를 호출할 때 값을 전달하지 않아도 됨+ 템플릿 매개변수도 기본값 정할 수 있음 (기본값= 데이터 형식)
- 클래스 템플릿의 객체를 생성할 때 템플릿 매개변수의 형식을 입력하지 않아도 기본으로 설정한 데이터 형식이 지정됨
- 클래스 템플릿 프렌드
- 클래스 템플릿도 프렌드 클래스와 함수를 가질 수 있음 (단, 인스턴스화 순서와 프렌드 규칙을 준수해야하므로 주의,,)
+) 프렌드 클래스는 friend로 지정되기 전 선언과 정의가 있어야 함
+) 특정 형식만 수용하는 템플릿template <typename C> void printout_friend_element (data_package<C> &data_object)
- 템플릿이나 클래스 템플릿에서 사용한 템플릿 매개변수는 소스코드를 작성할 때가 아니라 컴파일할 때 추론된 형식으로 인스턴스화 된다. - 템플릿 매개변수로 정의한 함수나 클래스의 멤버함수는 전체 매개변수의 데이터 형식을 지정하는 명시적 특수화와 일부 매개변수의 데이터 형식을 지정하는 부분 특수화가 있고, 부분 특수화는 클래스 템플릿에서만/ 명시적 특수화는 함수템플릿과 클래스 템플릿 모두에서 사용 가능