Presentation is loading. Please wait.

Presentation is loading. Please wait.

제 2장. C보다 나은 C++ I.

Similar presentations


Presentation on theme: "제 2장. C보다 나은 C++ I."— Presentation transcript:

1 제 2장. C보다 나은 C++ I

2 학습 목표 C++의 개선된 데이터 형 기능인 엄격한 형 검사, bool 형, 레퍼런스 형에 대해 알아본다.

3 개선된 데이터 형 엄격한 형 검사 bool 형 레퍼런스

4 엄격한 형 검사 (1) 명시적 함수 선언 C++에서는 함수 호출 전에 반드시 함수 선언 또는 정의가 필요하다.
함수 선언 시 리턴형과 인자형을 모두 명시해야 한다. void foo(); // 인자 없는 함수 선언 void main() { int len; len = strlen("Hello"); // 함수 선언이 없으므로 컴파일 에러 }

5 엄격한 형 검사 (2) void* 형의 형 변환 C++에서 char* 형, int* 형, double* 형 등은 언제든지 void* 형으로 형 변환 가능하다. C에서는 암시적으로 void* 형이 다른 포인터 형으로 형 변환 가능하지만, C++에서는 이런 형 변환을 허용하지 않는다.  명시적 형 변환 연산자 이용 int x = 10; void *pv = &x; // int* 형은 void* 형으로 자동 형 변환 가능 int *pi = malloc(sizeof(int)); // C++에서는 컴파일 에러 // C에서는 OK int *pi = (int*) malloc(sizeof(int)); // C, C++ 모두 OK

6 bool 형 (1) C 방식의 참, 거짓 C++의 bool 형
C에서는 0은 거짓(false), 0이 아닌 값은 참(true)으로 간주된다. C에는 부울 값(Boolean value)을 표현하기 위해서 정수형(int)을 사용하는 것이 일반적이다. C++의 bool 형 bool 형은 참 또는 거짓에 해당하는 값을 저장할 수 있는 1바이트 크기의 데이터 형이다. C++은 true, false 키워드를 제공한다. typedef int BOOL; // int 형을 BOOL 형으로 정의 #define TRUE 1 // 참에 해당하는 매크로 상수 #define FALSE 0 // 거짓에 해당하는 매크로 상수

7 bool 형 (2) struct POINT { int x; int y; };
bool IsEqual(const POINT* p1, const POINT* p2) if( ( p1->x == p2->x ) && ( p1->y == p2->y ) ) return true; return false; } int main() { POINT p1 = {10, 10}; POINT p2 = {20, 20}; POINT p3 = p1; if( IsEqual(&p1, &p2) ) cout << "p1 과 p2는 같습니다.\n"; else cout << "p1 과 p2는 다릅니다.\n"; if( IsEqual(&p1, &p3) ) cout << "p1 과 p3는 같습니다.\n"; cout << "p1 과 p3는 다릅니다.\n"; return 0; }

8 레퍼런스의 선언 레퍼런스 형은 한마디로 “다른 변수의 별명”이다. 레퍼런스의 선언
레퍼런스 수식어인 &를 레퍼런스를 선언할 때 지정한다. 레퍼런스가 어떤 변수의 별명인지 레퍼런스를 초기화하면서 지정한다. 레퍼런스를 선언할 때 데이터 형은 레퍼런스 변수가 참조하는 변수를 따라야 한다.  데이터 형이 일치하지 않으면 컴파일 에러

9 레퍼런스의 사용 예 int main() { int num = 10; int &ref = num; // ref는 num의 별명
cout << "num = " << num << endl; cout << "ref = " << ref << endl; // num의 값 출력 ref = 100; // num = 100;을 수행함 cout << "&num = " << &num << endl; cout << "&ref = " << &ref << endl; // num의 주소 출력 return 0; }

10 레퍼런스의 크기 (1) 레퍼런스는 자신만의 주소를 갖지 않는다.
포인터 변수는 다른 변수를 주소를 저장할 자신만의 메모리를 갖는다. 레퍼런스는 다른 변수에 대한 이름만 존재할 뿐 레퍼런스를 위한 메모리는 할당되지 않는다. 레퍼런스의 크기는 레퍼런스가 참조하는 변수의 크기를 따라 결정된다.

11 레퍼런스의 크기 (2) int main() { int num = 10; int &ref = num;
cout << "num의 크기 : " << sizeof(num) << endl; cout << "ref의 크기 : " << sizeof(ref) << endl; // num의 크기 cout << "char& 형의 크기 : " << sizeof(char&) << endl; // 1 바이트 cout << "short& 형의 크기 : " << sizeof(short&) << endl; // 2 바이트 cout << "int& 형의 크기 : " << sizeof(int&) << endl; // 4 바이트 cout << "double& 형의 크기 : " << sizeof(double&) << endl; // 8 바이트 return 0; }

12 레퍼런스 사용 시 주의사항 (1) 레퍼런스는 반드시 선언 시 초기화되어야 한다.
레퍼런스는 반드시 변수로 초기화해야지 상수로는 초기화할 수 없다. int &ref1; // 초기화하지 않았으므로 컴파일 에러 int &ref2 = 100; // 상수로 초기화했으므로 컴파일 에러

13 레퍼런스 사용 시 주의사항 (2) 레퍼런스의 데이터 형은 레퍼런스가 참조하는 변수의 데이터 형과 일치해야 한다.
레퍼런스가 참조하는 대상을 바꿀 수 없다. int num = 100; double &ref3 = num; // 레퍼런스 형이 잘못되었으므로 컴파일 에러 int x = 10, y = 20; int &ref4 = x; // ref4는 x의 별명 ref4 = y; // ref4는 y의 별명이라는 의미가 아니라 // x = y;의 의미

14 레퍼런스의 용도 (1) 변수의 이름이 복잡할 때 사용하기 쉬운 간단한 별명을 만들기 위해서 레퍼런스를 사용한다.
while( true ) { int &temp = arr[2*i+3*j+k]; // temp는 arr[2*i+3*j+k]의 별명 temp = temp * 2 + 1; if( temp > 1000 ) break; i++; ... }

15 레퍼런스의 용도 (2) 변수의 이름을 직접 사용할 수 없는 경우에 대신 별명을 사용한다. int main() {
int x = 10; // main의 지역 변수 x는 foo 함수에서는 // 사용 할 수 없다. foo(x); // 함수의 인자로 x를 전달한다. cout << "x = " << x << endl; // x = 11 } void foo(int &ref) // ref는 인자로 전달된 x의 별명이다. ref++; // ref는 x의 별명이므로, main 함수의 x를 증가한다.

16 레퍼런스에 의한 함수 인자 전달 함수의 인자 전달 방법을 결정하는 기준
함수를 호출할 때 넘겨준 인자를 함수 안에서 사용만 하고 변경하지 않을 때  값에 의한 전달 사용 함수를 호출할 때 넘겨준 인자를 함수 안에서 변경해야 할 때  포인터에 의한 전달이나 레퍼런스에 의한 전달 사용 함수의 처리 결과를 인자로 받아오고자 할 때 사용

17 포인터에 의한 전달 (1) void Swap(int *x, int *y) // 포인터에 의한 전달
{ int temp = *x; // 포인터 간접 참조 연산자 사용 *x = *y; *y = temp; } int main() { int a, b; cout << "두 수를 입력하세요 : "; cin >> a >> b; cout << "Swap 호출 전의 a = " << a << ", b = " << b << endl; Swap(&a , &b); // 함수 호출 시 변수의 주소를 전달 cout << "Swap 호출 후의 a = " << a << ", b = " << b << endl; return 0; v

18 포인터에 의한 전달 (2) 포인터에 의한 인자 전달 과정

19 레퍼런스에 의한 전달 (1) void Swap(int &x, int &y) // 레퍼런스에 의한 전달
{ int temp = x; // 레퍼런스를 직접 사용 x = y; y = temp; } int main() { int a, b; cout << "두 수를 입력하세요 : "; cin >> a >> b; cout << "Swap 호출 전의 a = " << a << ", b = " << b << endl; Swap(a , b); // 함수 호출 시 변수 전달 cout << "Swap 호출 후의 a = " << a << ", b = " << b << endl; return 0; v

20 레퍼런스에 의한 전달 (2) 레퍼런스에 의한 인자 전달 과정

21 레퍼런스에 의한 전달의 장점 레퍼런스에 의한 전달에서는 코드가 간결해진다.
함수를 호출할 때도 변수를 직접 넘겨주고 함수 안에서도 레퍼런스를 사용해서 직접 함수를 호출한 곳의 변수에 접근할 수 있기 때문 포인터에 의한 전달 방법에서는 의도하지 않는 문제가 발생하는 경우가 있다. void Swap(int *x, int *y); int main() { Swap(NULL, NULL); // 컴파일 에러는 아니지만 실행 에러 발생 } void Swap(int *x, int *y) { x++; // x는 포인터이므로 주소를 변경할 수 있다.

22 포인터와 레퍼런스의 차이점 차이점 포인터 레퍼런스 대상의 유무 대상이 없을 수도 있고 있을 수도 있다.
int *ptr1; // 컴파일 경고 int *ptr2 = NULL; // 널 포인터 반드시 참조하는 대상이 필요하다. int &ref1; // 컴파일 에러 대상의 변경 가리키는 대상을 바꿀 수 있다. int num1 = 10, num2 = 20; int *ptr = &num1; *ptr = 100; // num1 = 100; ptr = &num2; *ptr = 200; // num2 = 200; 가리키는 대상을 바꿀 수 없다. int num3 = 30, num4 = 40; int &ref = num3; ref = 300; // num3 = 300; ref = num4; // num3 = num4;

23 const 레퍼런스 (1) 레퍼런스를 선언할 때 const 키워드를 함께 사용하면 레퍼런스가 참조하는 변수의 값을 변경할 수 없다. 레퍼런스는 자신이 참조하는 변수에 읽기 전용의 접근(read-only access)만 가능하다. int data = 100; const int &ref = data; cout << ref; // cout << data;의 의미 ref = 200; // const 레퍼런스는 변경할 수 없으므로 컴파일 에러 data = 200; // data를 직접 변경하는 것은 가능하다.

24 const 레퍼런스 (2) const 레퍼런스는 함수의 인자로 구조체나 클래스 형을 넘길 때 유용하게 사용된다.
구조체를 인자로 전달할 때 레퍼런스로 전달하면 구조체를 복사하지 않고 별명으로만 전달한다. void Print(const STUDENT &s) { s.grade[0] = 0; // 함수 안에서 s를 변경할 수 없으므로 컴파일 에러 } int main() { STUDENT s1 = {“장동건”, 100, 90, 80, 99, 98}; Print(s1); // 레퍼런스에 의한 전달이므로 // Print 함수의 s는 s1의 별명임

25 개선된 함수 기능 인라인 함수 디폴트 인자 함수 오버로딩 함수 템플릿 v

26 인라인 함수(inline function)
인라인 함수란? 함수 호출 시 발생하는 오버헤드를 줄이기 위해서 함수를 호출하는 대신 함수가 호출되는 곳마다 함수의 코드를 복사하여 넣어주는 방법이다. 인라인 함수의 정의 함수의 선언이나 정의에 inline 키워드를 지정하면 된다. v

27 인라인 함수의 사용 예 inline int Add(int x, int y); int main() { int a, b;
cout << "두 정수를 입력하세요 : "; cin >> a >> b; cout << "a + b = " << Add(a, b) << endl; return 0; } inline int Add(int x, int y) return x + y; a + b

28 인라인 함수 사용 시 주의 사항 프로그래머가 inline으로 지정한다고 해서 항상 인라인 함수가 되는 것은 아니다.
재귀 함수나 가상 함수, 함수에 대한 포인터를 사용하는 경우에는 인라인 함수가 될 수 없다. 인라인 함수의 정의는 헤더 파일에 포함되어야 한다.

29 인라인 함수 vs 매크로 함수 매크로 함수와 인라인 함수의 차이점 매크로 함수의 문제점 인라인 함수를 사용하는 것이 좋다.
매크로 함수  선행처리기에 의한 문자열 대치 방식 인라인 함수  컴파일러에 의한 코드 대치 방식 매크로 함수의 문제점 연산자 우선 순위 문제 인자의 형 검사를 하지 않음 인라인 함수를 사용하는 것이 좋다.

30 디폴트 인자(default parameter)
디폴트 인자란? 함수를 호출할 때 인자를 생략하면 디폴트 값이 자동적으로 사용되도록 하는 방법이다. 디폴트 인자를 지정할 때는 함수의 선언에 지정한다. 디폴트 인자를 지정할 때는 인자형. 인자 이름 다음에 = 과 함께 디폴트 값을 지정한다. 디폴트 인자 void foo( char c, int i, double d = 0.5 ); int main() { foo('a', 10); // 디폴트 인자에 의해 foo('a', 10, 0.5); 호출 foo('b', 20, 1.0); } v

31 디폴트 인자 규칙 (1) 디폴트 인자를 지정할 때의 규칙은 디폴트 인자를 함수의 가장 오른쪽 인자부터 지정해야 한다는 것이다.
올바른 디폴트 인자 지정 예 잘못된 디폴트 인자 지정 예 void foo1(char c, int i, double d); void foo2(char c, int i, double d = 0.5); void foo3(char c, int i = 10, double d = 0.5); void foo4(char c = 'A', int i = 10, double d = 0.5); void foo5(char c, int i = 10, double d); void foo6(char c = 'A', int i, double d); void foo7(char c = 'A', int i = 10, double d); void foo8(char c = 'A', int i , double d = 0.5);

32 디폴트 인자 규칙 (2) 디폴트 인자를 사용할 때 규칙은 함수의 인자를 생략할 때 함수의 가장 오른쪽 인자부터 생략해야 한다는 것이다. 올바른 디폴트 인자 사용 예 잘못된 디폴트 인자 사용 예 void foo(char c = 'A', int i = 10, double d = 0.5); foo('A', 20); // 세 번째 인자만 생략함 foo('B'); // 두 번째, 세 번째 인자만 생략함 foo(); // 인자 모두를 생략함 void foo5(char c, int i = 10, double d); void foo6(char c = 'A', int i, double d); void foo7(char c = 'A', int i = 10, double d); void foo8(char c = 'A', int i , double d = 0.5);

33 함수 오버로딩 (function overloading)
함수 오버로딩이란? 이름은 같지만 인자의 개수나 데이터 형이 다른 함수를 여러 번 정의할 수 있는 기능이다. C++이 제공하는 함수 오버로딩을 이용하면 같은 이름을 갖는 함수를 여러 번 정의할 수 있다. 각각의 함수는 인자의 시그니처(인자의 개수나 인자의 데이터 형)가 반드시 달라야 한다. 함수 호출 시 전달된 인자의 데이터 형으로 컴파일러가 어떤 함수를 호출할 지를 결정한다. v

34 함수 오버로딩의 예 int GetMax(int a, int b) { return a > b ? a : b; }
char GetMax(char a, char b) int main() cout << GetMax(10, 20) << endl; // int GetMax(int, int); 호출 cout << GetMax('A', 'B') << endl; // char GetMax(char, char); 호출 v

35 함수 오버로딩 시 주의사항 (1) 인자의 이름만 다른 경우 오버로딩할 수 없다.
함수의 리턴형만 다른 경우 오버로딩할 수 없다. int foo1(int a, int b); // (1) int foo1(int x, int y); // (2) foo1(10, 20); int foo2(char c, int num); // (1) void foo2(char c, int num); // (2) foo2('A', 20); v

36 함수 오버로딩 시 주의사항 (2) 데이터 형과 해당 형의 레퍼런스 형으로 오버로딩된 경우 오버로딩할 수 없다.
typedef로 정의된 데이터 형에 대해 오버로딩된 경우 오버로딩할 수 없다. int foo3(int a, int b); // (1) int foo3(int &a, int &b); // (2) int x = 10, y = 20; foo3(x, y); v typedef unsigned int UINT; void foo4(UINT a, UINT b); // (1) void foo4(unsigned int a, unsigned int b); // (2) foo4(10, 20);

37 함수 오버로딩 시 주의사항 (3) 디폴트 인자에 의해서 인자의 개수가 같은 경우 오버로딩할 수 없다.
void foo5(int a); // (1) void foo5(int a, int b = 0); // (2) foo5(10); v

38 디폴트 인자 vs 함수 오버로딩 (1) 두 함수의 처리 과정이 비슷하고 한 함수가 다른 함수의 특별한 경우로 간주되는 경우  디폴트 인자 사용 int GetSum(int x, int y, int z = 0) // 디폴트 인자 지정 { return x + y + z; } int main() GetSum(10, 20); // GetSum(10, 20, 0); 호출 GetSum(10, 20, 30); v

39 디폴트 인자 vs 함수 오버로딩 (2) 두 함수의 구체적인 처리 과정이 다르지만 같은 함수 이름으로 표현될 수 있다는 공통점만 갖는 경우  함수 오버로딩 사용 int GetSum(int x, int y) // (1) { return x + y; } int GetSum(const int arr[], int size) // (2) int sum = 0; for(int i = 0 ; i < size ; i++) sum += arr[i]; return sum; v

40 함수 템플릿(function template)
함수 템플릿이란? 특정 데이터 형을 사용하는 대신 어떤 데이터 형이든 될 수 있는 범용형으로 정의된 함수를 말한다. 함수 템플릿은 직접 함수를 정의하는 대신 컴파일러에게 함수를 정의하는 방법을 알려준다. 컴파일러는 함수가 호출될 때 사용된 인자의 데이터 형을 검사하여 함수 템플릿으로부터 함수의 정의를 자동으로 생성한다.  함수 템플릿의 인스턴스화 v

41 함수 템플릿 함수 템플릿은 처리할 데이터의 데이터 형은 다양하지만, 처리 알고리즘은 동일한 함수를 만들 때 유용하다. v

42 함수 템플릿의 정의 함수 템플릿을 만들 때는 template 키워드를 함수의 선언과 정의 양쪽 모두에 지정한다.
함수 템플릿에서 범용형을 선언할 때는 typename 키워드를 사용한다. 범용형 template < typename T > T GetMax(T a, T b) { return a > b ? a : b; } v

43 함수 템플릿의 인스턴스화 함수 템플릿에서 사용된 범용형이 어떤 데이터 형인지를 알려줌으로써 함수 템플릿으로부터 함수 정의를 생성하는 것 범용형 대신 사용되는 데이터 형을 템플릿의 파라미터라고 한다. 암시적 인스턴스화 명시적 인스턴스화 cout << GetMax(10, 20) << endl; // T = int char ch = GetMax('A', 'B'); // T = char cout << GetMax(3.14, 10.5) << endl; // T = double cout << GetMax(5, 10.5) << endl; // 컴파일 에러 cout << GetMax<int>(5, 10.5) << endl; // T= int cout << GetMax<double>(5, 10.5) << endl; // T= double

44 정리 C++은 C에 비해 엄격한 형 검사를 수행한다.
C++은 참, 거짓을 나타내기 위한 데이터 형으로 bool 형을 제공한다. 레퍼런스는 다른 변수의 별명을 만들기 위한 기능이다. 레퍼런스에 의한 인자 전달 방법을 사용하면 함수 안에서 변경되어야 하는 인자를 전달할 수 있다. 즉, 함수의 처리 결과를 인자로 받아올 때 레퍼런스에 의한 인자 전달 방법을 사용한다. 인라인 함수는 함수 호출의 오버헤드를 줄이기 위해 함수가 호출되는 곳에 함수의 내용을 넣어준다. 인라인 함수를 사용하면 프로그램의 수행 속도를 빠르게 할 수 있다. 디폴트 인자는 함수 호출 시 인자를 생략할 수 있도록 미리 인자의 디폴트 값을 정해두는 것이다. 디폴트 인자를 지정할 때는 반드시 함수의 오른쪽 인자부터 지정해야 한다. 함수 오버로딩은 같은 이름을 갖는 함수를 여러 번 정의하는 기능이다. 함수 오버로딩을 하려면 반드시 인자의 개수나 데이터 형이 달라야 한다. 함수를 직접 정의하는 대신 범용형을 이용하여 함수 템플릿을 정의하면, 필요할 때마다 함수를 다시 정의하지 않고 함수의 정의를 자동으로 생성할 수 있다. 함수 템플릿은 처리 알고리즘은 같지만 사용되는 데이터 형이 다양한 함수를 정의할 때 유용하게 사용할 수 있다.


Download ppt "제 2장. C보다 나은 C++ I."

Similar presentations


Ads by Google