C++

14주차 강의 내용

wlsn 2024. 12. 3. 11:52

제너릭 프로그래밍을 아주 쉽게 설명해볼게요.

제너릭 프로그래밍은 "모든 타입을 위한 만능 상자"라고 생각하면 돼요.

예를 들어, 우리가 "사과"와 "바나나"를 담는 상자를 만든다고 해볼게요. 일반 상자는 사과만 담을 수 있지만, 만능 상자는 사과도 담고 바나나도 담을 수 있어요.

즉, 제너릭 프로그래밍은 특정한 데이터 타입에 국한되지 않고, 다양한 타입의 데이터를 담을 수 있는 방법이에요. 이렇게 하면 코드를 더 간단하고 재사용하기 쉽게 만들어 줍니다.

그래서 제너릭 프로그래밍을 사용하면 다양한 데이터에 대해 같은 로직을 쓸 수 있어서 편리해요!

 

뤼튼에게 generic programming을 물었을 때의 답

 

generic programming은 자료형이 실행될 때 결정된다.

#include <iostream>
using std::cout;
using std::endl;

int Max(int i, int j)
{
   return i>j ?i:j;
}
double Max(double i, double j)
{ 
   return i>j ?i:j;
}
char Max(char i, char j)
{
   return i>j ?i:j;
}
int main()
{
  cout<< "Max값은=" << Max(1,2)<<endl;
  cout<< "Max값은=" << Max(7.5,3.6)<<endl;
  cout<< "Max값은=" << Max('A','B');
  return 0;
}

 

#include <iostream>
using std::cout;
using std::endl;
template <class T>
T Max(T i, T j)
{
	return i > j ? i : j;
}
int main()
{
	cout << "Max값은=" << Max(1, 2) << endl;
	cout << "Max값은=" << Max(7.5, 3.6) << endl;
	cout << "Max값은=" << Max('A', 'B');
	return 0;
}

첫번째 소스를 generic programming으로 바꾼 결과다 int를 T로 바꾸고 template을 쓴다.

 

#include <iostream> // 입력과 출력을 위한 헤더 파일 포함
using std::cout;   // std 네임스페이스의 cout 사용
using std::endl;   // std 네임스페이스의 endl 사용

// 제너릭 함수 정의: T는 타입 매개변수
template <class T>
T Max(T i, T j) // 두 개의 같은 타입 인자를 받아서 최대값을 반환
{
    return i > j ? i : j; // i가 j보다 크면 i를, 그렇지 않으면 j를 반환
}

int main() // 프로그램의 시작점
{
    // 정수형 인자를 사용하여 Max 함수 호출
    cout << "Max값은=" << Max(1, 2) << endl; // 결과: 2

    // 실수형 인자를 사용하여 Max 함수 호출
    cout << "Max값은=" << Max(7.5, 3.6) << endl; // 결과: 7.5

    // 문자형 인자를 사용하여 Max 함수 호출
    cout << "Max값은=" << Max('A', 'B'); // 결과: B

    return 0; // 프로그램 종료
}

뤼튼에게 주석을 달아달라고 했을 때의 답이다.

첫번째는 정수형, 두번째는 실수형, 세번째는 문자형이다.

 

 

뤼튼에게 언어별로 generic programming을 알려달라고 했을 때의 답이다.

 

 

지금까지 했던 template으로 T로 바꿨었던 내용이 generic funtion이다.

 

#include <iostream>
using std::cout;
using std::endl;
template <typename T>
T Max(T i, T j)
{
	return i > j ? i : j;
}
int main()
{
	cout << "Max값은=" << Max(1, 2) << endl;
	cout << "Max값은=" << Max(7.5, 3.6) << endl;
	cout << "Max값은=" << Max('A', 'B');
	return 0;
}

이 소스처럼 class를 typename으로 바꿔도 내용은 제대로 나온다.

class 일반적으로 클래스 타입 매개변수를 정의할 때 사용됩니다.
typename 타입 매개변수를 좀 더 일반적으로 나타낼 때 사용됩니다. 특히, 템플릿의 중첩 타입을 참조할 때 더 자주 사용됩니다.

 

#include <iostream>
using std::cout;
using std::endl;

template <class T1, class T2, class T3> void fun(T1 x, T2 y, T3 z)
{     // 두 개의 매개변수 자료형이 T1과 T2로 다르다.
	cout << x << "  " << y << << " "<< z << endl;
}
int main()
{
	fun("Han", 30, 2); // T1은 문자열(const char *),T2는 정수형(int)
	fun(25, 50.5, 'a');  // T1은 정수형(int), T2는 double형 
	fun(1, 2, 1.5);
	fun('a', 3.5, 'b');
	return 0;
}

시험에서 class를 안써서 틀리는 경우가 종종 있다고 한다.

보통 결정되지 않은 자료형에서는 T1, T2, T3등 Tn으로 정의한다고 한다.

 

1단계 template 쓰고 T1, T2 정의하기

#include <iostream>
using std::cout;
using std::endl;

template<class T1, class T2>
class CCC1
{
private:
	int x;
	int y;
public:
	CCC1(int xx, int yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};
class CCC2
{
private:
	double x;
	double y;
public:
	CCC2(double xx, double yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};
class CCC3
{
private:
	char x;
	const char* y;
public:
	CCC3(char xx, const char* yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};

int main()
{
	CCC1 c1(10, 20); //int
	CCC2 c2(3.5, 5.5); //double
	CCC3 c3('I', "Love You!"); //char

	c1.Print();
	c2.Print();
	c3.Print();
	return 0;
}

 

2단계 template class 완성

#include <iostream>
using std::cout;
using std::endl;

template<class T1, class T2>
class CCC
{
private:
	T1 x;
	T2 y;
public:
	CCC(T1 xx, T2 yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};
int main()
{
	CCC1 c1(10, 20); //int
	CCC2 c2(3.5, 5.5); //double
	CCC3 c3('I', "Love You!"); //char

	c1.Print();
	c2.Print();
	c3.Print();
	return 0;
}

 

3단계 main 함수에서 에러 고치기

#include <iostream>
using std::cout;
using std::endl;

template<class T1, class T2>
class CCC
{
private:
	T1 x;
	T2 y;
public:
	CCC(T1 xx, T2 yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};
int main()
{
	CCC<int, int> c1(10, 20); //int
	CCC<double, double> c2(3.5, 5.5); //double
	CCC<char, const char *> c3('I', "Love You!"); //char

	c1.Print();
	c2.Print();
	c3.Print();
	return 0;
}

템플릿을 만들고 결정되지 않은 자료형은 객체 앞에 꺽쇠를 넣어 지정해줘야 한다.

 

#include <iostream> // 입력과 출력을 위한 헤더 파일 포함
using std::cout;   // std 네임스페이스의 cout 사용
using std::endl;   // std 네임스페이스의 endl 사용

// 두 개의 타입 매개변수를 받는 템플릿 클래스 CCC 정의
template<class T1, class T2>
class CCC {
private:
    T1 x; // 타입 T1의 변수 x
    T2 y; // 타입 T2의 변수 y
public:
    // 생성자: 두 개의 매개변수를 받아서 x와 y에 초기화
    CCC(T1 xx, T2 yy) { 
        x = xx; 
        y = yy; 
    }
    
    // Print 메서드: x와 y의 값을 출력
    void Print() { 
        cout << x << ',' << y << endl; 
    }
};

int main() {
    // int 타입의 x와 y를 가지는 CCC 객체 c1 생성
    CCC<int, int> c1(10, 20); // c1은 (10, 20)으로 초기화
    // double 타입의 x와 y를 가지는 CCC 객체 c2 생성
    CCC<double, double> c2(3.5, 5.5); // c2는 (3.5, 5.5)으로 초기화
    // char와 const char * 타입의 x와 y를 가지는 CCC 객체 c3 생성
    CCC<char, const char *> c3('I', "Love You!"); // c3는 ('I', "Love You!")으로 초기화

    // 각 객체의 Print 메서드 호출
    c1.Print(); // 출력: 10, 20
    c2.Print(); // 출력: 3.5, 5.5
    c3.Print(); // 출력: I, Love You!

    return 0; // 프로그램 종료
}

위 코드는 뤼튼에게 주석을 달아달라고 했을 때의 모습이다.

 

함수타입을 만들 때는 typename보단 class 를 더 선호하고 template을 만들 때는 class를 더 선호한다.

 

 

vector 컨테이너

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector <char> x;  //int x[10]와 차이
    // 여러 개 int형을 가지고 노는 배열 공간을 만들고 싶어요 
    x.push_back('a');
    x.push_back('b');
    x.push_back('c');
    for (int i = 0; i < x.size(); i++)
        cout << x[i] << endl;
    return 0;
}

길이가 자동으로 늘어나는 배열 int, double, char 등 자료형이 올 수 있음

 

예시 함수

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec; // 빈 벡터 생성

    vec.push_back(10);    // 10 추가
    vec.push_back(20);    // 20 추가
    vec.push_back(30);    // 30 추가

    std::cout << "Size: " << vec.size() << std::endl; // 크기 출력
    std::cout << "Front: " << vec.front() << std::endl; // 첫 번째 요소 출력
    std::cout << "Back: " << vec.back() << std::endl; // 마지막 요소 출력

    vec.pop_back();       // 마지막 요소 제거

    std::cout << "Size after pop: " << vec.size() << std::endl; // 크기 출력

    // 모든 요소 출력
    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " "; // 인덱스를 사용한 접근
    }
    std::cout << std::endl;

    return 0;
}

이 예시 코드에서는 std::vector를 생성하고, 요소를 추가 및 제거하며, 다양한 멤버 함수를 사용하는 방법을 보여줍니다. std::vector는 매우 유용한 동적 배열 구조로, 다양한 상황에서 자주 사용됩니다.

 

언어별로 예외처리(Exception handling)에 관한 표

 

실행할 때 발생할 수 있는 오류를 잡아주는 것을 예외처리라고 한다.

 

try로 묶고 throw로 던지고 catch로 받아서 처리한다.

언어별로 조금 다르지만 try는 대부분 비슷하다고 볼 수 있다.

 

예외처리 1단계

실행할 때 예외가 발생 할 수 있는 줄을 묶는다.

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
void Div(double ja, double mo)
{
	try {
		cout << "결과:" << ja / mo << endl;
	}
}
int main()
{
	double x, y;

	cout << "분자를 입력하세요=";
	cin >> x;
	cout << "분모를 입력하세요=";
	cin >> y;
	Div(x, y);
	return 0;
}

 

예외처리 2단계

예욋값을 던짐(throw와 if를 추가)

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
void Div(double ja, double mo)
{
	try {
		if(mo == 0) throw mo;
		cout << "결과:" << ja / mo << endl;
	}
}
int main()
{
	double x, y;

	cout << "분자를 입력하세요=";
	cin >> x;
	cout << "분모를 입력하세요=";
	cin >> y;
	Div(x, y);
	return 0;
}

 

예외처리 3단계(완성)

오류가 발생되면 catch로 이동하여 처리됨

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
void Div(double ja, double mo)
{
	try {
		if(mo == 0) throw mo;
		cout << "결과:" << ja / mo << endl;
	}
	catch (double) {
		cout << "오류 : 0으로 나눌 수 없음";
	}
}
int main()
{
	double x, y;

	cout << "분자를 입력하세요=";
	cin >> x;
	cout << "분모를 입력하세요=";
	cin >> y;
	Div(x, y);
	return 0;
}

 

catch는 예욋값의 자료형을 써준다.

catch는 여러 개를 만들어 다양한 자료형을 넣어 그때마다 다르게 사용 할 수 있다.

 

 

참조자(reference) 매우중요

 

#include <iostream>
using std::cout;
using std::endl;
int main(void)
{
    int x = 10;
    int &rx = x;
    cout << x << " " << rx << endl;
    rx = rx + 10;
    cout << x << " " << rx << endl; //참조자(rx)에 변화를 주면 그 타켓(x)도 변함
    x = x + 10;
    cout << x << " " << rx << endl; //타켓(x)에 변화를 주면 그 참조자(rx)도 변함
    return 0;
}

참조자를 붙인다면 rx=x가 됨으로 둘다 동시에 변하게 되어 10 10 20 20 30 30이 나오게 된다.

 

콘솔 파일 입출력은 시험범위는 19p, 24p, 27p, 

 

width는 출력폭을 지정

precision은 소수점 자릿수 지정

fill은 공백구간에 ch 문자를 채움

fixed는 소수점 이하의 자릿수를 다룸

#include <iostream>
using namespace std;
int main() {
    cout << "디폴트\n";
    cout.fill('*');
    cout.width(10);
    cout << -50 << endl;
    cout << "[ * fill ]\n";
    cout.fill('#');
    cout.width(10);
    cout << -50 << endl;
    cout.width(10);
    cout << 100.25 << endl;
    cout.width(10);
    cout << "HanSH" << endl;
    cout.fill(' ');
    cout.precision(5);  //소수점을 제외한 전체 자리수 
    cout << 12.34567 << endl;
    cout << fixed;  //소수점 이하의 자리수만 다루게 함 
    cout.precision(1);
    cout << 12.34567 << endl;
    return 0;
}

 

이 소스가 더 쉬워보이지만 <iomanip>를 포함시켜줘야된다.

 

 

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ofstream fout("a1.txt");
    fout << "abcddatw\n";
    cout << "abc\n";
}

 

ofstream을 써 메모장으로 출력하게 만든 소스다.

cout은 abc로 출력이 되지만 메모장에 저장되는 값은 abcddatw다.

 

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    int x;

    ofstream fout("a.txt");
    fout << 2345;
    fout.close();

    ifstream fin("a.txt");
    fin >> x;
    cout << x;
}

파일을 만들었으면 다른 파일을 실행하기 전에 무조건 close로 닫아야 한다.

위 파일은 ofstream으로 a라는 메모장파일을 만들어 2345라는 값을 저장한 후 출력하게 하는 코드다.

 

ppt 출처 : Smlie Han

'C++' 카테고리의 다른 글

C++과 함께하는 우주 탐험  (0) 2024.12.10
14주차 예습과제  (0) 2024.12.03
13주차 강의 내용  (0) 2024.11.26
13주차 예습과제  (0) 2024.11.26
12주차 강의내용  (0) 2024.11.19