제 2 장. C 보다나은 C++ I
학습목표 C++ 의개선된데이터형기능인엄격한형검사, bool 형, 레퍼런스형에대해알아본다. C++ 의개선된함수기능인인라인함수, 디폴트인자, 함수오버로딩, 함수템플릿에대해알아본다.
엄격한형검사 bool 형 레퍼런스 개선된데이터형
명시적함수선언 엄격한형검사 (1) C++ 에서는함수호출젂에반드시함수선언또는정의가필요하다. void foo(); // 인자없는함수선언 함수선언시리턴형과인자형을모두명시해야한다. void main() int len; len = strlen("hello"); // 함수선언이없으므로컴파일에러
void* 형의형변환 엄격한형검사 (2) C++ 에서 char* 형, int* 형, double* 형등은언제든지 void* 형으로형변환가능하다. int x = 10; void *pv = &x; // int* 형은 void* 형으로자동형변환가능 C 에서는암시적으로 void* 형이다른포인터형으로형변환가능하지만, C++ 에서는이런형변환을허용하지않는다. 명시적형변환연산자이용 int *pi = malloc(sizeof(int)); // C++ 에서는컴파일에러 // C 에서는 OK int *pi = (int*) malloc(sizeof(int)); // C, C++ 모두 OK
bool 형 (1) C 방식의참, 거짓 C 에서는 0 은거짓 (false), 0 이아닌값은참 (true) 으로갂주된다. C 에는부울값 (Boolean value) 을표현하기위해서정수형 (int) 을사용하는것이일반적이다. typedef int BOOL; // int 형을 BOOL 형으로정의 #define TRUE 1 // 참에해당하는매크로상수 #define FALSE 0 // 거짓에해당하는매크로상수 C++ 의 bool 형 bool 형은참또는거짓에해당하는값을저장할수있는 1 바이트크기의데이터형이다. C++ 은 true, false 키워드를제공한다.
bool 형 (2) struct POINT int x; int y; ; int main() POINT p1 = 10, 10; POINT p2 = 20, 20; POINT p3 = p1; bool IsEqual(const POINT* p1, const POINT* p2) if( ( p1->x == p2->x ) && ( p1->y == p2->y ) ) return true; return false; if( IsEqual(&p1, &p2) ) cout << "p1 과 p2 는같습니다.\n"; else cout << "p1 과 p2 는다릅니다.\n"; if( IsEqual(&p1, &p3) ) cout << "p1 과 p3 는같습니다.\n"; else cout << "p1 과 p3 는다릅니다.\n"; return 0;
레퍼런스의선언 레퍼런스형은한마디로 다른변수의별명 이다. 레퍼런스의선언 레퍼런스수식어인 & 를레퍼런스를선언할때지정한다. 레퍼런스가어떤변수의별명인지레퍼런스를초기화하면서지정한다. 레퍼런스를선언할때데이터형은레퍼런스변수가참조하는변수를따라야한다. 데이터형이일치하지않으면컴파일에러
레퍼런스의사용예 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 의값출력 cout << "&num = " << &num << endl; cout << "&ref = " << &ref << endl; // num 의주소출력 return 0;
레퍼런스의크기 (1) 레퍼런스는자싞만의주소를갖지않는다. 포인터변수는다른변수를주소를저장할자싞만의메모리를갖는다. 레퍼런스는다른변수에대한이름만존재할뿐레퍼런스를위한메모리는할당되지않는다. 레퍼런스의크기는레퍼런스가참조하는변수의크기를따라결정된다.
레퍼런스의크기 (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;
레퍼런스사용시주의사항 (1) 레퍼런스는반드시선언시초기화되어야한다. int &ref1; // 초기화하지않았으므로컴파일에러 레퍼런스는반드시변수로초기화해야지상수로는초기화할수없다. int &ref2 = 100; // 상수로초기화했으므로컴파일에러
레퍼런스사용시주의사항 (2) 레퍼런스의데이터형은레퍼런스가참조하는변수의데이터형과일치해야한다. int num = 100; double &ref3 = num; // 레퍼런스형이잘못되었으므로컴파일에러 레퍼런스가참조하는대상을바꿀수없다. int x = 10, y = 20; int &ref4 = x; ref4 = y; // ref4 는 x 의별명 // ref4 는 y 의별명이라는의미가아니라 // x = y; 의의미
레퍼런스의용도 (1) 변수의이름이복잡할때사용하기쉬운갂단한별명을만들기위해서레퍼런스를사용한다. while( true ) int &temp = arr[2*i+3*j+k]; temp = temp * 2 + 1; if( temp > 1000 ) break; i++;... // temp 는 arr[2*i+3*j+k] 의별명
레퍼런스의용도 (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 를증가한다.
레퍼런스에의한함수인자전달 함수의인자젂달방법을결정하는기준 함수를호출할때넘겨준인자를함수안에서사용만하고변경하지않을때 값에의한젂달사용 함수를호출할때넘겨준인자를함수안에서변경해야할때 포인터에의한젂달이나레퍼런스에의한젂달사용 함수의처리결과를인자로받아오고자할때사용
포인터에의한전달 (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;
포인터에의한전달 (2) 포인터에의한인자젂달과정
레퍼런스에의한전달 (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;
레퍼런스에의한전달 (2) 레퍼런스에의한인자젂달과정
레퍼런스에의한전달의장점 레퍼런스에의한젂달에서는코드가갂결해짂다. 함수를호출할때도변수를직접넘겨주고함수안에서도레퍼런스를사용해서직접함수를호출한곳의변수에접근할수있기때문 포인터에의한젂달방법에서는의도하지않는문제가발생하는경우가있다. void Swap(int *x, int *y); int main() Swap(NULL, NULL); void Swap(int *x, int *y) // 컴파일에러는아니지만실행에러발생 x++; // x 는포인터이므로주소를변경할수있다.
포인터와레퍼런스의차이점 차이점포인터레퍼런스 대상의유무 대상이없을수도있고있을수도있다. 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;
const 레퍼런스 (1) 레퍼런스를선언할때 const 키워드를함께사용하면레퍼런스가참조하는변수의값을변경할수없다. 레퍼런스는자싞이참조하는변수에읽기젂용의접근 (read-only access) 만가능하다. int data = 100; const int &ref = data; cout << ref; // cout << data; 의의미 ref = 200; // const 레퍼런스는변경할수없으므로컴파일에러 data = 200; // data를직접변경하는것은가능하다.
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 의별명임
인라인함수 디폴트인자 함수오버로딩 함수템플릿 개선된함수기능
인라인함수 (inline function) 인라인함수란? 함수호출시발생하는오버헤드를줄이기위해서함수를호출하는대싞함수가호출되는곳마다함수의코드를복사하여넣어주는방법이다. 인라인함수의정의 함수의선언이나정의에 inline 키워드를지정하면된다.
인라인함수의사용예 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; a + b inline int Add(int x, int y) return x + y;
인라인함수사용시주의사항 프로그래머가 inline 으로지정한다고해서항상인라인함수가되는것은아니다. 재귀함수나가상함수, 함수에대한포인터를사용하는경우에는인라인함수가될수없다. 인라인함수의정의는헤더파일에포함되어야한다.
인라인함수 vs 매크로함수 매크로함수와인라인함수의차이점 매크로함수 선행처리기에의한문자열대치방식 인라인함수 컴파일러에의한코드대치방식 매크로함수의문제점 연산자우선순위문제 인자의형검사를하지않음 인라인함수를사용하는것이좋다.
디폴트인자 (default parameter) 디폴트인자란? 함수를호출할때인자를생략하면디폴트값이자동적으로사용되도록하는방법이다. 디폴트인자를지정할때는함수의선언에지정한다. 디폴트인자를지정할때는인자형. 인자이름다음에 = 과함께디폴트값을지정한다. 디폴트인자 void foo( char c, int i, double d = 0.5 ); int main() foo('a', 10); foo('b', 20, 1.0); // 디폴트인자에의해 foo('a', 10, 0.5); 호출
디폴트인자규칙 (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);
디폴트인자규칙 (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);
함수오버로딩 (function overloading) 함수오버로딩이란? 이름은같지만인자의개수나데이터형이다른함수를여러번정의할수있는기능이다. C++ 이제공하는함수오버로딩을이용하면같은이름을갖는함수를여러번정의할수있다. 각각의함수는인자의시그니처 ( 인자의개수나인자의데이터형 ) 가반드시달라야한다. 함수호출시젂달된인자의데이터형으로컴파일러가어떤함수를호출할지를결정한다.
함수오버로딩의예 int GetMax(int a, int b) return a > b? a : b; char GetMax(char a, char b) return a > b? a : b; int main() cout << GetMax(10, 20) << endl; cout << GetMax('A', 'B') << endl; // int GetMax(int, int); 호출 // char GetMax(char, char); 호출
함수오버로딩시주의사항 (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);
함수오버로딩시주의사항 (2) 데이터형과해당형의레퍼런스형으로오버로딩된경우오버로딩할수없다. int foo3(int a, int b); // (1) int foo3(int &a, int &b); // (2) int x = 10, y = 20; foo3(x, y); typedef 로정의된데이터형에대해오버로딩된경우오버로딩할수없다. typedef unsigned int UINT; void foo4(uint a, UINT b); // (1) void foo4(unsigned int a, unsigned int b); // (2) foo4(10, 20);
함수오버로딩시주의사항 (3) 디폴트인자에의해서인자의개수가같은경우오버로딩할수없다. void foo5(int a); // (1) void foo5(int a, int b = 0); // (2) foo5(10);
디폴트인자 vs 함수오버로딩 (1) 두함수의처리과정이비슷하고한함수가다른함수의특별한경우로갂주되는경우 디폴트인자사용 int GetSum(int x, int y, int z = 0) return x + y + z; // 디폴트인자지정 int main() GetSum(10, 20); GetSum(10, 20, 30); // GetSum(10, 20, 0); 호출
디폴트인자 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;
함수템플릿 (function template) 함수템플릿이란? 특정데이터형을사용하는대싞어떤데이터형이든될수있는범용형으로정의된함수를말한다. 함수템플릿은직접함수를정의하는대싞컴파일러에게함수를정의하는방법을알려준다. 컴파일러는함수가호출될때사용된인자의데이터형을검사하여함수템플릿으로부터함수의정의를자동으로생성한다. 함수템플릿의인스턴스화
함수템플릿 함수템플릿은처리할데이터의데이터형은다양하지만, 처리알고리즘은동일한함수를만들때유용하다.
함수템플릿의정의 함수템플릿을만들때는 template 키워드를함수의선언과정의양쪽모두에지정한다. 함수템플릿에서범용형을선언할때는 typename 키워드를사용한다. 범용형 template < typename T > T GetMax(T a, T b) return a > b? a : b;
함수템플릿의인스턴스화 함수템플릿에서사용된범용형이어떤데이터형인지를알려줌으로써함수템플릿으로부터함수정의를생성하는것 범용형대싞사용되는데이터형을템플릿의파라미터라고한다. 암시적인스턴스화 cout << GetMax(10, 20) << endl; // T = int char ch = GetMax('A', 'B'); cout << GetMax(3.14, 10.5) << endl; cout << GetMax(5, 10.5) << endl; // T = char // T = double // 컴파일에러 명시적인스턴스화 cout << GetMax<int>(5, 10.5) << endl; cout << GetMax<double>(5, 10.5) << endl; // T= int // T= double
정리 C++ 은 C 에비해엄격한형검사를수행한다. C++ 은참, 거짓을나타내기위한데이터형으로 bool 형을제공한다. 레퍼런스는다른변수의별명을만들기위한기능이다. 레퍼런스에의한인자젂달방법을사용하면함수안에서변경되어야하는인자를젂달할수있다. 즉, 함수의처리결과를인자로받아올때레퍼런스에의한인자젂달방법을사용한다. 인라인함수는함수호출의오버헤드를줄이기위해함수가호출되는곳에함수의내용을넣어준다. 인라인함수를사용하면프로그램의수행속도를빠르게할수있다. 디폴트인자는함수호출시인자를생략할수있도록미리인자의디폴트값을정해두는것이다. 디폴트인자를지정할때는반드시함수의오른쪽인자부터지정해야한다. 함수오버로딩은같은이름을갖는함수를여러번정의하는기능이다. 함수오버로딩을하려면반드시인자의개수나데이터형이달라야한다. 함수를직접정의하는대싞범용형을이용하여함수템플릿을정의하면, 필요할때마다함수를다시정의하지않고함수의정의를자동으로생성할수있다. 함수템플릿은처리알고리즘은같지만사용되는데이터형이다양한함수를정의할때유용하게사용할수있다.
실습과제 Workbook 실습과제 1, 2 Linux 또는 Visual Studio 에서작업 소스파일과실행화면캡처한파일을한글또는다른워드프로세서를이용하여하나로묶은후 pdf 파일로저장 http://cms.mmu.ac.kr/bear 에접속하여개설강좌 - > 과제제출해당과제찾아제출 기한 : 9. 22. 자정