[문과 코린이의 IT 기록장] C,C++ - 상수 (상수와 '함수, 포인터 변수, 클래스, &, 오버로딩'에 관한 내용 이해)
1. 상수란?
: 처음 정의시 그 값이 바로 주어지고, 한 번 저장된 값은 영원히 바뀌지 않는 것을 의미한다.
: 즉, 데이터 초기화가 이루어지면, 그 값을 바꿀 수 없도록 해 주는 것이다.
* 따라서 const변수는 데이터 영역에 들어가게 되고, 컴파일과 동시에 데이터 영역 메모리로 올라가 버린다.
1) 상수의 정의 방법
: const (상수의 형) (상수 이름) = (상수의 값) ;
2) 상수 예시
Const int a = 3; // int형 상수 a는, 3의 값을 지닌다. 이 a의 값은 절대 변경될 수 없다.
2. 상수 정의 시 주의사항
1) 상수는 선언과 동시에 초기화되어야 한다.
- 상수는 처음에 한 번 저장된 값은 절대로 변하지 않는 특징을 가진다.
- 그렇기 때문에 처음 상수의 값과 관련해서 실행될 때, 정의해주지 않는다면 오류가 발생하게 된다.
2) 상수의 값은 바꿀 수 없다.
- 상수는 어떠한 경우에도 값을 변경할 수 없는 불멸의 데이터이다. 따라서 이 코드는 오류가 발생한다.
3. 상수와 함수
1) const 사용 함수
ex )
int ShowPrint(int *num) const { ... }
- 이 함수는 const로 상수화 되어 있다.
- 즉 이 함수를 통해 값이 이루어지는 것을 방지하겠다는 뜻이다.
- 또한 이 함수는 const로 선언된 변수들 만이 사용될 수 있다는 뜻이다.
- cout이 사용될 수 있는 이유는 , cout이 ostream 클래스의 객체이고, 이 클래스의 cout 함수들은 많은 함수들을 오버로딩하고 있는 형태로 되어 있기 때문이다. (그 중에는 const로 선언된 함수도 포함되어 있다.) |
2) const함수 초기화 방법
- const함수는
1. 선언과 동시에 초기화 해주거나,
2. 맴버 이니셜라이저를 통해 초기화가 가능하다.
ex )
3) 함수 const 매개변수
[ 파라미터(매개변수)의 종류 ] (1) In-Parameter #include <iostream> using namespace std;
void Output(int num){ cout<<num<<endl; } int main(){ int nNum = 10; Output(nNum); return 0; } - In-Parameter은 함수에 nNum이라는 데이터를 전달하는 것을 말한다. - 즉, 값전달, 주소전달, 레퍼런스 전달이 모두 가능하다.
(2) Out-Parameter #include <iostream> - Out-Parameter은 함수에 의해 nNum이라는 데이터 값이 변경되는 것을 말한다. - 즉, 주소전달, 레퍼런스전달만 가능하다. |
[ 매개변수에서의 const ]
In-Parameter로 포인터 혹은 레퍼런스를 사용하고 싶은데, 값 변경의 위험이 느껴질 때, 매개변수 앞에 const를 붙여 상수화시켜준다.
ex 1 )
- 함수 내에서 num 값의 변경을 시도하려고 할 때, 에러가 발생되어서 값 변경을 불가능하게 한다.
cf ) 이는 맴버 변수를 변경하지 못하게 만드는, 클래스에서만 사용되는 (ex. void output(int &num) const)와는 다른 것이다.
ex 2)
- 클래스 형식이 In-Parameter로 전달될 때, 복사 생성자가 호출되는 오버헤드를 줄이기 위해 &를 붙이게 된다.
- 이 상황에서, 전달받는 객체의 값을 변경하지 못하게 하기 위해 const를 붙인다.
- 즉, In-Parameter로 쓰이는 포인터와 레퍼런스는 꼭 const를 쓸 필요가 있다.
4. 포인터 변수와 const
- 포인터 변수에는 자료형 앞 혹은 뒤에 붙여서 사용하는, 두 가지 방법이 있다.
1) const int *p1 = &a;
p1 = &b; // 컴파일 가능
*p1 = 10; // 컴파일 오류 발생
- 가르키는 '주소의 값'을 변경하지 못한다.
- 즉, 대상체만 변경할 수 있으며, 대상체의 값을 변경하지는 못한다.
2) int * const p2 = &b;
p2 = &a; // 컴파일 오류 발생
*p2 = 10; // 컴파일 가능
- 가르키는 '주소(주소값)'를 변경할 수 없다.
- 즉, 대상체를 변경할 수 없으며, 대상체의 값만 변경할 수 있다.
[ 요약 ]
5. 상수와 클래스
ex )
- 이 코드에서, 상수 객체의 맴버 변수값을 바꾸는 행위는, 모두 const 위반을 하므로 컴파일 에러를 발생시킨다.
1) 상수 객체에 대한 이해
Const A a;
// 생성자를 통해 상수 객체를 생성
// 따라서 이 객체는, '상수 맴버 함수'만 명시적으로 호출할 수 있다.
2) 상수 맴버 함수에 대한 이해
- 상수 맴버 함수란? // 이 구문은 클래스에서만 쓰인다.
: 호출한 객체의 맴버 변수를 변경할 수 없는 맴버 함수를 말한다. 상수 맴버 함수는 함수 선언에서 매개변수 목록 뒤에 const를 붙여준다.
ex) int SetNum() const { ... }
- 상수 맴버 함수의 특징
1. 상수 맴버 함수에서, 맴버 변수 값을 바꾸는 행위를 할 수 없다.
2. 상수 맴버 함수 안에서 비-상수 맴버 함수를 호출할 수 없다.
3. 상수 클래스 객체에 대해서도 호출할 수 있다.
ex ) int SetNum() const {
num = 3; // 컴파일 에러 1. 상수 맴버 함수에서, 맴버 변수 값을 바꾸는 행위를 할 수 없다.
GetNum(3); // 컴파일 에러 2. 상수 맴버 함수 안에서, 비-상수 맴버 함수를 호출할 수 없다.
return num;
}
3) 클래스 생성자에서의 상수
- C++ 클래스의 생성자는 맴버 변수를 초기화할 수 있어야 하므로, const 키워드의 사용을 허용하지 않는다.
4) const를 이용한 함수의 활용 예제
ex )
(1) int sum () const{ // 상수 맴버 함수
// const로 선언된 sum함수.
// const 변수는 내부에서 사용할 수 있지만, 값에 대한 변경은 안된다.
cout<< "return n1 + n2" <<endl;
return n1 + n2;
// 따라서 n1 + n2를 반환해 주는 것이 가능하다. (n1, n2 모두 const변수이기 때문)
}
(2) const int sum(int num1, int num2){ // 반환형에 사용되는 const(상수)
// 반환값이 const로 선언되어 있다.
// 따라서 반환값으로 전달되어 값을 저장할 때는 반드시 const 변수로 받아야 한다.
cout<< "return num1 + num2" <<endl;
return num1 + num2; // 이를 저장할 변수는, cosnt 변수여야 한다.
}
6. 상수 참조 (const reference)
- 참조로 전달(Pass by reference)의 장점
: 값으로 전달의 경우 복사본이 만들어지게 되는데, 복사본이 필요하지 않은 경우가 대부분이므로, 참조로 전달을 사용해 불필요한 복사를 하지 않도록 도울 수 있음.
- const reference의 장점
: 프로그래머가 함수 안에서 전달된 인수를 변경할 가능성을 막기 위해, 상수로 참조를 만들면 효과적임
- 함수의 인자로 클래스의 객체를 전달할 수 있으며, 전달할 때 상수 참조를 이용하는 것이 일반적이다.
ex )
- PrintDate()함수 내에서의, date객체(인자로 받는 객체의 별칭)는 상수 객체로 취급되게 된다.
- 이 date객체가 GetYear(), GetMonth(), GetDay() 맴버 함수를 호출하게 되는데, 이 맴버함수들은 모두 상수 함수여야 한다.
- 즉, 상수 객체에 대해 비-상수 맴버 함수를 호출하면 컴파일 오류가 발생하므로, 상수 객체에 대해서는 상수 맴버 함수를 호출해야 한다.
7. 상수 맴버 함수의 오버로딩 vs 비-상수 맴버 함수의 오버로딩
- 같은 맴버 함수에 대해서는 상수 버전과, 비-상수 버전으로 나누어 오버로딩이 가능하다.
- 맴버 함수의 상수 버전은 상수 객체에서 호출되며, 비-상수 버전은 비-상수 객체에서 호출된다.
8. 리터럴 vs 상수
상수 | 리터럴 | |
공통점 | 변하지 않는 값 (데이터) | |
차이점 | cf) 변하지 않는 변수를 뜻하는 것이기 때문에, 숫자가 올 수 있을뿐만 아니라, 객체, 유도형과 같은 데이터 등을 넣을 수 있다. |
인스턴스(클래스 데이터)는 리터럴이 될 수 없다. 보통 인스턴스는 동적으로 사용하기 위해 작성되므로, 값이 언제 바뀔지 모른다. |
* 유의사항 |