C++ (함수 인자의 새로운 용법, 오버로딩 과정 확인)
내장형 하드웨어/C++ / 2011. 11. 9. 11:13
- 예제(main.c, g++ 컴파일)
→ 위의 예제는 gcc로는 컴파일 되지 않고 g++로 컴파일 가능하다.
→ 함수 인자에서 "int B=3"과 같은 경우, 즉 인자에 값을 미리 주는 경우는 오른쪽 부터 주었을 때만 가능하다.
→ void test(int A=1, int B) 와 같이 선언하면 컴파일 에러가 발생한다.
→ 즉, void test(int A=1, int B=3) or void test(int A, int B=3)의 경우에만 컴파일이 가능하다.
→ 호출할 때의 값이 최우선이며, 다음으로 인자의 값을 우선한다.
→ cl로 컴파일 했을 때도 같은 결과를 호출한다.(확장자를 cpp로 변경하여 컴파일)
- 오버 로딩 (-> iostream.h를 가진 main1.c 파일을 작성하여 테스트)
→ 같은 이름을 가진 세개의 함수(void test()를 생성한다. 여기서 인자는 각각, (int, int), (int, float), (int)의 세가지로 한다.
→ main에서 각각의 함수를 호출하여 중간파일을 생성해본다.(g++ --save-temps main1 main1.c)
→ 소스 작성
1
3
1
3.1
1
→ 함수의 이름이 같으면 생길 수 있는 문제를 고려해서 컴파일러는 함수의 이름을 바꾼다.(명칭 부호화), 중간파일(.s)를 살펴보면 아래와 같은 것을 볼 수 있다.
→ cl로 중간파일 생성하여 확인(cl main.cpp /FA)
→ main.asm 확인
#include<stdio.h>
void test(int A, int B)
{
printf("%d\n", A);
printf("%d\n", B);
return ;
}
int main()
{
test(1, 2);
test(3);
test();
return 0;
}
→ 출력 결과는void test(int A, int B)
{
printf("%d\n", A);
printf("%d\n", B);
return ;
}
int main()
{
test(1, 2);
test(3);
test();
return 0;
}
→ 위의 예제는 gcc로는 컴파일 되지 않고 g++로 컴파일 가능하다.
→ 함수 인자에서 "int B=3"과 같은 경우, 즉 인자에 값을 미리 주는 경우는 오른쪽 부터 주었을 때만 가능하다.
→ void test(int A=1, int B) 와 같이 선언하면 컴파일 에러가 발생한다.
→ 즉, void test(int A=1, int B=3) or void test(int A, int B=3)의 경우에만 컴파일이 가능하다.
→ 호출할 때의 값이 최우선이며, 다음으로 인자의 값을 우선한다.
→ cl로 컴파일 했을 때도 같은 결과를 호출한다.(확장자를 cpp로 변경하여 컴파일)
- 오버 로딩 (-> iostream.h를 가진 main1.c 파일을 작성하여 테스트)
→ 같은 이름을 가진 세개의 함수(void test()를 생성한다. 여기서 인자는 각각, (int, int), (int, float), (int)의 세가지로 한다.
→ main에서 각각의 함수를 호출하여 중간파일을 생성해본다.(g++ --save-temps main1 main1.c)
→ 소스 작성
#include<iostream.h>
// 오버 로딩 test
void test(int A, int B)
{
cout << A << endl;
cout << B << endl;
}
void test(int A, float B)
{
cout << A << endl;
cout << B << endl;
}
void test(int A)
{
cout << A << endl;
}
int main()
{
test(1, 3);
test(1, 3.1f);
test(1);
return 0;
}
→ 출력 결과// 오버 로딩 test
void test(int A, int B)
{
cout << A << endl;
cout << B << endl;
}
void test(int A, float B)
{
cout << A << endl;
cout << B << endl;
}
void test(int A)
{
cout << A << endl;
}
int main()
{
test(1, 3);
test(1, 3.1f);
test(1);
return 0;
}
1
3
1
3.1
1
→ 함수의 이름이 같으면 생길 수 있는 문제를 고려해서 컴파일러는 함수의 이름을 바꾼다.(명칭 부호화), 중간파일(.s)를 살펴보면 아래와 같은 것을 볼 수 있다.
→ 살펴보면 test() 함수는 3번 호출에 각각 test__Fii, test__Fif, test__Fi의 3가지로 이름이 바뀌어 있는 것을 확인할 수 있다.(명칭 부호화) 함수 이름뒤에 __(언더바)가 붙어 있고 그 뒤에 F, 그리고 인자에 따라 int형은 i, float 형은 f로 이름지어져 있는 것을 확인할 수 있다.
→ cl로 중간파일 생성하여 확인(cl main.cpp /FA)
→ main.asm 확인
PUBLIC _main
_TEXT SEGMENT
_main PROC NEAR
; Line 19
push ebp
mov ebp, esp
; Line 20
push 3
push 1
call ?test@@YAXHH@Z ; test
add esp, 8
; Line 21
push 1078355558 ; 40466666H
push 1
call ?test@@YAXHM@Z ; test
add esp, 8
; Line 22
push 1
call ?test@@YAXH@Z ; test
add esp, 4
; Line 24
xor eax, eax
; Line 25
pop ebp
ret 0
_main ENDP
_TEXT ENDS
_TEXT SEGMENT
_main PROC NEAR
; Line 19
push ebp
mov ebp, esp
; Line 20
push 3
push 1
call ?test@@YAXHH@Z ; test
add esp, 8
; Line 21
push 1078355558 ; 40466666H
push 1
call ?test@@YAXHM@Z ; test
add esp, 8
; Line 22
push 1
call ?test@@YAXH@Z ; test
add esp, 4
; Line 24
xor eax, eax
; Line 25
pop ebp
ret 0
_main ENDP
_TEXT ENDS
→ cl 컴파일러의 경우에는
?test@@YAXHH@Z
?test@@YAXHM@Z
?test@@YAXH@Z
?test@@YAXHM@Z
?test@@YAXH@Z
의 3가지로 이름이 바뀐 것을 확인할 수 있다.
→ 여기서 문제는 cl 컴파일러로 컴파일 해서 확인한 결과와 g++ 컴파일러로 컴파일 한 결과 test 이름이 바뀐 형식이 다르다는 것을 알 수 있다. 즉, 명칭 부호의 적용 방식이 다른데 이로 인해 라이브러리를 만들어 사용한다고 했을 때 컴파일러가 다른 것에 따른 문제가 발생할 수 있다.
→ 이를 위해 컴파일러가 함수 이름 변경을 하지 못하게 막는 기법을 알아보면
→ 소스의 함수 앞에 extern "C" 를 붙여본다.
→ 위처럼 해당 하는 함수의 이름이 test로 되어 있는 것을 확인할 수 있다.
→ 즉, extern "C" 를 사용하여 컴파일러가 함수의 이름을 바꾸는 것을 막을 수도 있는데, 중복해서 같은 이름의 함수에 사용할 수 는 없다. 왜냐면 컴파일러가 기계어로 변환하는 과정에서 구별할 수 없기 때문이다.
→ 따라서 g++의 경우 컴파일 에러가 발생한다.
→ Cl의 경우 중복 컴파일도 가능한데 단, 이때는 함수의 이름이 둘다 명칭부호 형식에 따라 바뀐다.
→ 여기서 문제는 cl 컴파일러로 컴파일 해서 확인한 결과와 g++ 컴파일러로 컴파일 한 결과 test 이름이 바뀐 형식이 다르다는 것을 알 수 있다. 즉, 명칭 부호의 적용 방식이 다른데 이로 인해 라이브러리를 만들어 사용한다고 했을 때 컴파일러가 다른 것에 따른 문제가 발생할 수 있다.
→ 이를 위해 컴파일러가 함수 이름 변경을 하지 못하게 막는 기법을 알아보면
→ 소스의 함수 앞에 extern "C" 를 붙여본다.
extern "C" void test(int A)
{
cout << A << endl;
}
→ 위와 같고 중간파일을 생성하여 확인하면{
cout << A << endl;
}
→ 위처럼 해당 하는 함수의 이름이 test로 되어 있는 것을 확인할 수 있다.
call ?test@@YAXHH@Z ; test add esp, 8 ; Line 22 push 1078355558 ; 40466666H push 1 call ?test@@YAXHM@Z ; test add esp, 8 ; Line 23 push 1 call _test add esp, 4 |
→ 따라서 g++의 경우 컴파일 에러가 발생한다.
→ Cl의 경우 중복 컴파일도 가능한데 단, 이때는 함수의 이름이 둘다 명칭부호 형식에 따라 바뀐다.
※ 오버로딩 규칙 재확인
→ test(char A)
→ test(int A)
→ test('A');으로 호출할 경우 역시 에러가 발생한다.(애매하다. 즉, 이렇게 사용하면 안된다.)
※ 참고로 포인터에 NULL을 넣지 말고 0 을 넣자. NULL은 C에서 (void *)0으로 디파인 되어 있고 이것은 C++에서 형변환 문제를 발생시킬 수 있다. C는 형변환이 너그럽지만 C++이나 자바는 엄격하여 에러를 발생시킬 수 있다.
- 전역변수의 동적 초기화
→ 전역변수는 컴파일 시점에 값을 초기화 한다.(code, data, bss는 컴파일 과정에 메모리에 생성)
→ 지역변수는 실행될때 생성된다.(heap, stack)
→ 지역변수에 static을 사용하면 정적 변수가 되어 컴파일 단계에 초기화가 된다.
( → static int A = 10이라고 main() 내에 선언되면 이 변수는 컴파일시 10이 들어간다.)
→ 만약 C에서 전역 변수를 동적으로 사용하려 하면 에러가 발생할 것이다.
main2.c: 12: initializer element is not constant
→ 단, g++으로는 컴파일이 되는데, C++에서는 전역변수의 동적 초기화가 가능한 것을 알 수 있다.
→ 실행해 보면
→ C++도 C와 같이 main()이 엔트리 포인트는 맞지만 반드시 제일 처음 실행되는 함수가 아니다.(C는 반드시 제일 처음 호출)
→ 전역 변수를 최초 할당하는데 변수가 일반 함수를 필요로 할 경우 main() 보다 먼저 호출한다.(C에서는 불가능)
→ 즉, main() 실행전에 에러가 나서 문제가 발생할 수도 있다.
※ .cxx도 C++의 확장자
- 클래스(구조체를 확장한 것)
자료의 추상화
→ 프로그램을 보호하면서 함수를 이용해서 프로그래밍 한다.
→ 함수 첫번째 줄만 사용자에게 제공하며, 정해진 함수만 사용하도록 유도한다.
→ 자료형을 감싸서 함부로 접근하지 못하게 한다.
→ test(char A)
→ test(int A)
→ test('A');으로 호출할 경우 역시 에러가 발생한다.(애매하다. 즉, 이렇게 사용하면 안된다.)
※ 참고로 포인터에 NULL을 넣지 말고 0 을 넣자. NULL은 C에서 (void *)0으로 디파인 되어 있고 이것은 C++에서 형변환 문제를 발생시킬 수 있다. C는 형변환이 너그럽지만 C++이나 자바는 엄격하여 에러를 발생시킬 수 있다.
- 전역변수의 동적 초기화
→ 전역변수는 컴파일 시점에 값을 초기화 한다.(code, data, bss는 컴파일 과정에 메모리에 생성)
→ 지역변수는 실행될때 생성된다.(heap, stack)
→ 지역변수에 static을 사용하면 정적 변수가 되어 컴파일 단계에 초기화가 된다.
( → static int A = 10이라고 main() 내에 선언되면 이 변수는 컴파일시 10이 들어간다.)
→ 만약 C에서 전역 변수를 동적으로 사용하려 하면 에러가 발생할 것이다.
#include<stdio.h>
int test(void)
{
int iNum;
printf("숫자를 입력하세요.\n");
scanf("%d", &iNum);
return iNum;
}
int A = test();
int main()
{
printf("%d\n", A);
return 0;
}
→ 위의 예제를 gcc로 컴파일 하면 에러이다.int test(void)
{
int iNum;
printf("숫자를 입력하세요.\n");
scanf("%d", &iNum);
return iNum;
}
int A = test();
int main()
{
printf("%d\n", A);
return 0;
}
main2.c: 12: initializer element is not constant
→ 단, g++으로는 컴파일이 되는데, C++에서는 전역변수의 동적 초기화가 가능한 것을 알 수 있다.
→ 실행해 보면
→ C++도 C와 같이 main()이 엔트리 포인트는 맞지만 반드시 제일 처음 실행되는 함수가 아니다.(C는 반드시 제일 처음 호출)
→ 전역 변수를 최초 할당하는데 변수가 일반 함수를 필요로 할 경우 main() 보다 먼저 호출한다.(C에서는 불가능)
→ 즉, main() 실행전에 에러가 나서 문제가 발생할 수도 있다.
※ .cxx도 C++의 확장자
- 클래스(구조체를 확장한 것)
자료의 추상화
→ 프로그램을 보호하면서 함수를 이용해서 프로그래밍 한다.
→ 함수 첫번째 줄만 사용자에게 제공하며, 정해진 함수만 사용하도록 유도한다.
#include<iostream.h>
#include<stdlib.h>
struct emb
{
int *item;
int iNum;
};
// 구조체 초기화 함수
bool embInit(emb *stVal)
{
// 분홍색 값은 리터럴(문자상수)이다.
if(0 == stVal) // Null 을 입력받으면
{
return false;
}
stVal->item = 0;
stVal->iNum = 0;
return true;
}
bool embInit(emb *stVal, unsigned int uiNum)
{
// 분홍색 값은 리터럴(문자상수)이다.
if(0 == stVal) // Null 을 입력받으면
{
return false;
}
else if(0 == uiNum)
{
return false;
}
// item에 동적 할당
stVal->item = (int *)malloc(sizeof(int)*uiNum);
// 동적 할당을 못 받았을 경우
if(0 == stVal->item)
{
return false;
}
stVal->iNum = uiNum;
return true;
}
void embFree(emb *stVal)
{
if(0 == stVal)
{
return ;
}
if(0 == stVal->item)
{
return ;
}
free(stVal->item);
stVal->item = 0;
stVal->iNum = 0;
}
int main()
{
emb OBJ;
// 자료의 추상화
// OBJ에 함수만이 접근하고 main()은 접근하지 않는다.
// 즉, 함수를 통해서 간접적으로 건드리게 하는데
// 이것을 OBJ가 추상화 되었다. 자료가 추상화 되었다고 한다.
embInit(&OBJ); // 0으로 초기화 하는 함수
embInit(&OBJ, 300); // 동적할당 및 전체 계수(1200byte)
embFree(&OBJ);
return 0;
}
→ 함수를 통해서 OBJ에 접근하게 한다.#include<stdlib.h>
struct emb
{
int *item;
int iNum;
};
// 구조체 초기화 함수
bool embInit(emb *stVal)
{
// 분홍색 값은 리터럴(문자상수)이다.
if(0 == stVal) // Null 을 입력받으면
{
return false;
}
stVal->item = 0;
stVal->iNum = 0;
return true;
}
bool embInit(emb *stVal, unsigned int uiNum)
{
// 분홍색 값은 리터럴(문자상수)이다.
if(0 == stVal) // Null 을 입력받으면
{
return false;
}
else if(0 == uiNum)
{
return false;
}
// item에 동적 할당
stVal->item = (int *)malloc(sizeof(int)*uiNum);
// 동적 할당을 못 받았을 경우
if(0 == stVal->item)
{
return false;
}
stVal->iNum = uiNum;
return true;
}
void embFree(emb *stVal)
{
if(0 == stVal)
{
return ;
}
if(0 == stVal->item)
{
return ;
}
free(stVal->item);
stVal->item = 0;
stVal->iNum = 0;
}
int main()
{
emb OBJ;
// 자료의 추상화
// OBJ에 함수만이 접근하고 main()은 접근하지 않는다.
// 즉, 함수를 통해서 간접적으로 건드리게 하는데
// 이것을 OBJ가 추상화 되었다. 자료가 추상화 되었다고 한다.
embInit(&OBJ); // 0으로 초기화 하는 함수
embInit(&OBJ, 300); // 동적할당 및 전체 계수(1200byte)
embFree(&OBJ);
return 0;
}
→ 자료형을 감싸서 함부로 접근하지 못하게 한다.
'내장형 하드웨어 > C++' 카테고리의 다른 글
C++ - 객체 소멸 순서, 객체 생성(정적, 동적 할당-malloc, new) (0) | 2011.11.15 |
---|---|
C++ - car class 추가, 다중상속, 유도(derivation), 타입 (0) | 2011.11.14 |
C++ 생성자, 소멸자, 상속 (0) | 2011.11.11 |
C++ 클래스, 접근속성, scope 연산자(::), inline (0) | 2011.11.10 |
객체지향, C++ 첫시간(cout, endl, 오버로딩) (0) | 2011.11.08 |